summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordhebeha <dhebeha.mj71@wipro.com>2020-08-21 12:57:41 +0530
committerdhebeha <dhebeha.mj71@wipro.com>2020-09-01 12:38:26 +0530
commit58926cce98f4c590a98e042c94c7ea0ddeff4672 (patch)
tree8e4b7e5a8d9010cdb09bdef388fbd8cd1154a8a8
parent8e23179dd67da6517382369d579e62f2435d02c8 (diff)
Add NSSI candidate
-Add generic flow for NxI candidate -Add default attributes Issue-ID: OPTFRA-801 Signed-off-by: dhebeha <dhebeha.mj71@wipro.com> Change-Id: I869ebcd35d4c2436676868b26006ca991b34e538
-rw-r--r--conductor/conductor/controller/translator.py29
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/aai.py271
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/candidates/candidate.py5
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/candidates/nxi_candidate.py28
-rw-r--r--conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py81
-rw-r--r--conductor/conductor/tests/unit/data/plugins/inventory_provider/first_level_filter.json5
-rw-r--r--conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_candidate.json52
-rw-r--r--conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_demand_list.json17
-rw-r--r--conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_response.json3
-rw-r--r--conductor/conductor/tests/unit/data/plugins/inventory_provider/slice_profile.json19
-rw-r--r--conductor/conductor/tests/unit/data/plugins/inventory_provider/slice_profile_converted.json18
-rw-r--r--conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py66
-rw-r--r--conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai_utils.py95
-rw-r--r--conductor/conductor/tests/unit/solver/optimizer/constraints/test_threshold.py2
-rw-r--r--conductor/tox.ini4
15 files changed, 465 insertions, 230 deletions
diff --git a/conductor/conductor/controller/translator.py b/conductor/conductor/controller/translator.py
index c0d7e29..83c71ed 100644
--- a/conductor/conductor/controller/translator.py
+++ b/conductor/conductor/controller/translator.py
@@ -26,14 +26,14 @@ import uuid
import six
import yaml
-from conductor import __file__ as conductor_root
-from conductor import messaging
-from conductor import service
-from conductor.common import threshold
+from conductor import __file__ as conductor_root
from conductor.common.music import messaging as music_messaging
-from conductor.data.plugins.triage_translator.triage_translator_data import TraigeTranslatorData
+from conductor.common import threshold
from conductor.data.plugins.triage_translator.triage_translator import TraigeTranslator
+from conductor.data.plugins.triage_translator.triage_translator_data import TraigeTranslatorData
+from conductor import messaging
+from conductor import service
from oslo_config import cfg
from oslo_log import log
@@ -48,8 +48,8 @@ INVENTORY_TYPES = ['cloud', 'service', 'transport', 'vfmodule', 'nssi']
DEFAULT_INVENTORY_PROVIDER = INVENTORY_PROVIDERS[0]
CANDIDATE_KEYS = ['candidate_id', 'cost', 'inventory_type', 'location_id',
'location_type']
-DEMAND_KEYS = ['filtering_attributes', 'passthrough_attributes', 'candidates', 'complex', 'conflict_identifier',
- 'customer_id', 'default_cost', 'excluded_candidates',
+DEMAND_KEYS = ['filtering_attributes', 'passthrough_attributes', 'default_attributes', 'candidates', 'complex',
+ 'conflict_identifier', 'customer_id', 'default_cost', 'excluded_candidates',
'existing_placement', 'flavor', 'inventory_provider',
'inventory_type', 'port_key', 'region', 'required_candidates',
'service_id', 'service_resource_id', 'service_subscription',
@@ -142,7 +142,7 @@ class Translator(object):
self._translation = None
self._valid = False
self._ok = False
- self.triageTranslatorData= TraigeTranslatorData()
+ self.triageTranslatorData = TraigeTranslatorData()
self.triageTranslator = TraigeTranslator()
# Set up the RPC service(s) we want to talk to.
self.data_service = self.setup_rpc(self.conf, "data")
@@ -504,7 +504,7 @@ class Translator(object):
"demands": {
name: requirements,
},
- "plan_info":{
+ "plan_info": {
"plan_id": self._plan_id,
"plan_name": self._plan_name
},
@@ -517,10 +517,8 @@ class Translator(object):
for requirement in requirements:
required_candidates = requirement.get("required_candidates")
excluded_candidates = requirement.get("excluded_candidates")
- if (required_candidates and
- excluded_candidates and
- set(map(lambda entry: entry['candidate_id'],
- required_candidates))
+ if (required_candidates and excluded_candidates and set(map(lambda entry: entry['candidate_id'],
+ required_candidates))
& set(map(lambda entry: entry['candidate_id'],
excluded_candidates))):
raise TranslatorException(
@@ -939,9 +937,7 @@ class Translator(object):
if not self.valid:
raise TranslatorException("Can't translate an invalid template.")
- request_type = self._parameters.get("request_type") \
- or self._parameters.get("REQUEST_TYPE") \
- or ""
+ request_type = self._parameters.get("request_type") or self._parameters.get("REQUEST_TYPE") or ""
self._translation = {
"conductor_solver": {
@@ -952,7 +948,6 @@ class Translator(object):
"demands": self.parse_demands(self._demands),
"objective": self.parse_optimization(self._optmization),
"constraints": self.parse_constraints(self._constraints),
- "objective": self.parse_optimization(self._optmization),
"reservations": self.parse_reservations(self._reservations),
}
}
diff --git a/conductor/conductor/data/plugins/inventory_provider/aai.py b/conductor/conductor/data/plugins/inventory_provider/aai.py
index 821219c..9defe94 100644
--- a/conductor/conductor/data/plugins/inventory_provider/aai.py
+++ b/conductor/conductor/data/plugins/inventory_provider/aai.py
@@ -27,17 +27,21 @@ import uuid
from oslo_config import cfg
from oslo_log import log
+
from conductor.common import rest
from conductor.data.plugins import constants
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.nxi_candidate import NxI
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.inventory_provider import hpa_utils
+from conductor.data.plugins.inventory_provider.utils import aai_utils
from conductor.data.plugins.triage_translator.triage_translator import TraigeTranslator
-from conductor.i18n import _LE, _LI
+from conductor.i18n import _LE
+from conductor.i18n import _LI
LOG = log.getLogger(__name__)
@@ -81,7 +85,7 @@ AAI_OPTS = [
help='Certificate Authority Bundle file in pem format. '
'Must contain the appropriate trust chain for the '
'Certificate file.'),
- #TODO(larry): follow-up with ONAP people on this (AA&I basic auth username and password?)
+ # TODO(larry): follow-up with ONAP people on this (AA&I basic auth username and password?)
cfg.StrOpt('username',
default='',
help='Username for AAI.'),
@@ -252,8 +256,7 @@ class AAI(base.InventoryProviderBase):
if len(physical_location_list) > 0:
physical_location_id = physical_location_list[0].get('d_value')
- if not (cloud_region_version and
- cloud_region_id):
+ if not (cloud_region_version and cloud_region_id):
continue
rel_link_data_list = \
self._get_aai_rel_link_data(
@@ -291,8 +294,7 @@ class AAI(base.InventoryProviderBase):
country = complex_info.get('country')
complex_name = complex_info.get('complex-name')
- if not (latitude and longitude and city and country
- and complex_name):
+ if not (latitude and longitude and city and country and complex_name):
keys = ('latitude', 'longitude', 'city', 'country',
'complex_name')
missing_keys = \
@@ -451,12 +453,11 @@ class AAI(base.InventoryProviderBase):
return regions
def _get_flavors(self, cloud_owner, cloud_region_id):
+ '''Fetch all flavors of a given cloud regions specified using {cloud-owner}/{cloud-region-id} composite key
+
+ :return flavors_info json object which list of flavor nodes and its children - HPACapabilities:
'''
- Fetch all flavors of a given cloud regions specified using
- {cloud-owner}/{cloud-region-id} composite key
- :return flavors_info json object which list of flavor nodes and
- its children - HPACapabilities:
- '''
+
LOG.debug("Fetch all flavors and its child nodes HPACapabilities")
flavor_path = constants.FLAVORS_URI % (cloud_owner, cloud_region_id)
path = self._aai_versioned_path(flavor_path)
@@ -476,9 +477,8 @@ class AAI(base.InventoryProviderBase):
# Remove extraneous flavor information
return flavors_info
else:
- LOG.error(_LE("Received Error while fetching flavors from" \
- "Cloud-region {}/{}").format(cloud_owner,
- cloud_region_id))
+ LOG.error(_LE("Received Error while fetching flavors from Cloud-region {}/{}").format(cloud_owner,
+ cloud_region_id))
return
def _get_aai_path_from_link(self, link):
@@ -507,7 +507,6 @@ class AAI(base.InventoryProviderBase):
for vnf in generic_vnf:
related_to = "service-instance"
search_key = "customer.global-customer-id"
- match_key = "customer.global-customer-id"
rl_data_list = self._get_aai_rel_link_data(
data=vnf,
related_to=related_to,
@@ -714,9 +713,7 @@ class AAI(base.InventoryProviderBase):
Used by resolve_demands
"""
- if restricted_value and \
- restricted_value is not '' and \
- candidate[attribute_name] != restricted_value:
+ if restricted_value and restricted_value != '' and candidate[attribute_name] != restricted_value:
LOG.info(_LI("Demand: {} "
"Discarded {} candidate as "
"it doesn't match the "
@@ -745,10 +742,9 @@ class AAI(base.InventoryProviderBase):
for attribute_key, attribute_values in template_attributes.items():
- if attribute_key and (attribute_key == 'service-type' or
- attribute_key == 'equipment-role' or
- attribute_key == 'model-invariant-id' or
- attribute_key == 'model-version-id'):
+ if attribute_key and \
+ (attribute_key == 'service-type' or attribute_key == 'equipment-role'
+ or attribute_key == 'model-invariant-id' or attribute_key == 'model-version-id'):
continue
match_type = 'any'
@@ -761,12 +757,11 @@ 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
+ # 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 \
@@ -785,7 +780,8 @@ class AAI(base.InventoryProviderBase):
body = response.json()
return body.get("generic-vnf", [])
- def resolve_v_server_for_candidate(self, candidate_id, location_id, 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))
@@ -895,8 +891,8 @@ 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_id, v_server, cloud_owner, cloud_region_id, service_type,
- demand_name, triage_translator_data):
+ 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(
data=v_server,
@@ -1020,8 +1016,8 @@ class AAI(base.InventoryProviderBase):
if "cloud-region-version" in region:
return self._get_version_from_string(region["cloud-region-version"])
- def resolve_global_customer_id_for_vnf(self, candidate_id, location_id, vnf, customer_id, service_type, demand_name,
- triage_translator_data):
+ 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"
match_key = "customer.global-customer-id"
@@ -1042,8 +1038,8 @@ class AAI(base.InventoryProviderBase):
return None
return rl_data_list[0]
- def resolve_service_instance_id_for_vnf(self, candidate_id, location_id, vnf, customer_id, service_type, demand_name,
- triage_translator_data):
+ 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"
match_key = "customer.global-customer-id"
@@ -1135,10 +1131,10 @@ 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: make it consistent for dash and underscore
+ default_attributes = requirement.get('default_attributes')
+ # TODO(XYZ): may need to support multiple service_type and customer_id in the futrue
+ # TODO(XYZ): make it consistent for dash and underscore
if filtering_attributes:
# catch equipment-role and service-type from template
equipment_role = filtering_attributes.get('equipment-role')
@@ -1194,17 +1190,18 @@ class AAI(base.InventoryProviderBase):
LOG.debug("Region information is not available in cache")
for region_id, region in regions.items():
# Pick only candidates from the restricted_region
- info = Candidate.build_candidate_info('aai', inventory_type, self.conf.data.cloud_candidate_cost,
+ 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'
+ if self.check_sriov_automation(cloud['cloud_region_version'], name, info['candidate_id']):
+ other['sriov_automation'] = 'true'
+ else:
+ other['sriov_automation'] = '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()
@@ -1218,11 +1215,13 @@ class AAI(base.InventoryProviderBase):
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'])):
+ 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')
+ reason='attributes and match invetory '
+ 'attributes')
continue
if conflict_identifier:
@@ -1318,7 +1317,7 @@ class AAI(base.InventoryProviderBase):
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['cloud_region_version'], name, info['candidate_id']) else 'false'
# Second level query to get the pserver from vserver
complex_list = list()
@@ -1327,8 +1326,9 @@ class AAI(base.InventoryProviderBase):
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,
+ 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
@@ -1410,7 +1410,8 @@ class AAI(base.InventoryProviderBase):
vnf_dict[vnf_id] = vnf
# INFO
- info = Candidate.build_candidate_info('aai', inventory_type, self.conf.data.service_candidate_cost,
+ 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)
@@ -1437,7 +1438,8 @@ class AAI(base.InventoryProviderBase):
else: # vserver is for a different customer
self.triage_translator.collectDroppedCandiate('', '', name,
triage_translator_data,
- reason="candidate is for a different customer")
+ reason="candidate is for a different"
+ " customer")
continue
vf_modules_list = self.resolve_vf_modules_for_generic_vnf('', '', vnf, name,
@@ -1465,16 +1467,19 @@ class AAI(base.InventoryProviderBase):
vserver_info = dict()
vserver_info['vservers'] = list()
complex_list = list()
- 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):
+ 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'] = 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)
+ 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:
@@ -1483,8 +1488,8 @@ class AAI(base.InventoryProviderBase):
# COMPLEX
complex_info = self.build_complex_info_for_candidate(info['candidate_id'],
- cloud['location_id'], vnf, complex_list,
- service_type, name,
+ cloud['location_id'], vnf,
+ complex_list, service_type, name,
triage_translator_data)
if complex_info.get("complex_name") is None:
continue
@@ -1495,7 +1500,7 @@ class AAI(base.InventoryProviderBase):
additional_fields=other, vlan=vlan_info)
candidate = vf_module_candidate.convert_nested_dict_to_dict()
- ##add vf-module parameters for filtering
+ # 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
@@ -1576,7 +1581,8 @@ class AAI(base.InventoryProviderBase):
candidate_uniqueness, vnf_service_instance_id,
service_resource_id)
else:
- self.triage_translator.collectDroppedCandiate('', other['location_id'], name, triage_translator_data,
+ self.triage_translator.collectDroppedCandiate('', other['location_id'], name,
+ triage_translator_data,
reason="service-instance-id error ")
continue
@@ -1600,8 +1606,8 @@ class AAI(base.InventoryProviderBase):
)
if len(rel_link_data_list) > 1:
- self.triage_translator.collectDroppedCandiate(info['candidate_id'], other['location_id'], name,
- triage_translator_data,
+ self.triage_translator.collectDroppedCandiate(info['candidate_id'], other['location_id'],
+ name, triage_translator_data,
reason="rel_link_data_list error")
continue
@@ -1613,9 +1619,10 @@ class AAI(base.InventoryProviderBase):
LOG.debug("{} complex information not "
"available from A&AI - {}".
format(name, complex_link))
- self.triage_translator.collectDroppedCandiate(info['candidate_id'], other['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(
@@ -1626,13 +1633,16 @@ class AAI(base.InventoryProviderBase):
LOG.debug("{} complex information not "
"available from A&AI - {}".
format(name, complex_link))
- self.triage_translator.collectDroppedCandiate(info['candidate_id'], other['location_id'], name,
+ self.triage_translator.collectDroppedCandiate(info['candidate_id'],
+ other['location_id'], name,
triage_translator_data,
- reason="complex information not available from A&AI")
+ reason="complex information not "
+ "available from A&AI")
continue # move ahead with the next vnf
complex_info = self.build_complex_dict(complex_info, inventory_type)
- transport_candidate = Transport(info=info, zone=zone_info, complex=complex_info, additional_fiels=other)
+ 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)
@@ -1641,9 +1651,12 @@ class AAI(base.InventoryProviderBase):
elif inventory_type == 'nssi':
if filtering_attributes and model_invariant_id:
- resolved_demands[name].extend(self.get_nssi_candidates(filtering_attributes,
- model_invariant_id, model_version_id,
- service_role, candidate_uniqueness))
+ second_level_match = aai_utils.get_first_level_and_second_level_filter(filtering_attributes,
+ "service_instance")
+ aai_response = self.get_nxi_candidates(filtering_attributes)
+ resolved_demands[name].extend(self.filter_nxi_candidates(aai_response, second_level_match,
+ default_attributes,
+ candidate_uniqueness, inventory_type))
else:
LOG.error("Unknown inventory_type "
@@ -1659,7 +1672,8 @@ class AAI(base.InventoryProviderBase):
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 \
+ 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)
@@ -1783,7 +1797,8 @@ class AAI(base.InventoryProviderBase):
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")
+ 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)
@@ -1795,7 +1810,8 @@ class AAI(base.InventoryProviderBase):
body = response.json()
return body
- def match_region(self, candidate, restricted_region_id, restricted_complex_id, demand_name, triage_translator_data):
+ def match_region(self, candidate, restricted_region_id, restricted_complex_id, demand_name,
+ triage_translator_data):
if self.match_candidate_attribute(
candidate,
"location_id",
@@ -1852,92 +1868,41 @@ class AAI(base.InventoryProviderBase):
directives = None
return directives
- def get_nssi_candidates(self, filtering_attributes, model_invariant_id, model_version_id, service_role,
- candidate_uniqueness):
- raw_path = ('nodes/service-instances' +
- '?model-invariant-id={}'.format(model_invariant_id) +
- ('&model-version-id={}'.format(model_version_id) if model_version_id else '') +
- ('&service-role={}'.format(service_role) if service_role else '') +
- '&depth=2')
-
+ def get_nxi_candidates(self, filtering_attributes):
+ raw_path = 'nodes/service-instances' + aai_utils.add_query_params_and_depth(filtering_attributes, "2")
path = self._aai_versioned_path(raw_path)
aai_response = self._request('get', path, data=None)
if aai_response is None or aai_response.status_code != 200:
return None
+ if aai_response.json():
+ return aai_response.json()
- return self.filter_nssi_candidates(aai_response.json(), filtering_attributes, candidate_uniqueness)
-
- def filter_nssi_candidates(self, response_body, filtering_attributes, candidate_uniqueness):
-
+ def filter_nxi_candidates(self, response_body, filtering_attributes, default_attributes, candidate_uniqueness,
+ type):
candidates = list()
- if filtering_attributes and response_body is not None:
- nssi_instances = response_body.get("service-instance", [])
-
- for nssi_instance in nssi_instances:
- inventory_attributes = dict()
- inventory_attributes["orchestration-status"] = nssi_instance.get('orchestration-status')
- inventory_attributes["service-role"] = nssi_instance.get('service-role')
-
- if self.match_inventory_attributes(filtering_attributes, inventory_attributes,
- nssi_instance.get('service-instance-id')):
-
- nsi_link = self._get_aai_rel_link(nssi_instance, 'service-instance')
-
- nsi_info = self.get_nsi_info(nsi_link)
-
- slice_profiles = nssi_instance.get('slice-profiles').get('slice-profile')
- slice_profile = min(slice_profiles, key=lambda x: x['latency'])
-
- candidate = dict()
- candidate['candidate_id'] = nssi_instance.get('service-instance-id')
- candidate['instance_name'] = nssi_instance.get('service-instance-name')
- candidate['cost'] = self.conf.data.nssi_candidate_cost
- candidate['candidate_type'] = 'nssi'
- candidate['inventory_type'] = 'nssi'
- candidate['inventory_provider'] = 'aai'
- candidate['domain'] = nssi_instance.get('environment-context')
- candidate['latency'] = slice_profile.get('latency')
- candidate['max_number_of_ues'] = slice_profile.get('max-number-of-UEs')
- candidate['coverage_area_ta_list'] = slice_profile.get('coverage-area-TA-list')
- candidate['ue_mobility_level'] = slice_profile.get('ue-mobility-level')
- candidate['resource_sharing_level'] = slice_profile.get('resource-sharing-level')
- candidate['exp_data_rate_ul'] = slice_profile.get('exp-data-rate-UL')
- candidate['exp_data_rate_dl'] = slice_profile.get('exp-data-rate-DL')
- candidate['area_traffic_cap_ul'] = slice_profile.get('area-traffic-cap-UL')
- candidate['area_traffic_cap_dl'] = slice_profile.get('area-traffic-cap-DL')
- candidate['activity_factor'] = slice_profile.get('activity-factor')
- candidate['e2e_latency'] = slice_profile.get('e2e-latency')
- candidate['jitter'] = slice_profile.get('jitter')
- candidate['survival_time'] = slice_profile.get('survival-time')
- candidate['exp_data_rate'] = slice_profile.get('exp-data-rate')
- candidate['payload_size'] = slice_profile.get('payload-size')
- candidate['traffic_density'] = slice_profile.get('traffic-density')
- candidate['conn_density'] = slice_profile.get('conn-density')
- candidate['reliability'] = slice_profile.get('reliability')
- candidate['service_area_dimension'] = slice_profile.get('service-area-dimension')
- candidate['cs_availability'] = slice_profile.get('cs-availability')
- candidate['uniqueness'] = candidate_uniqueness
- if nsi_info:
- candidate['nsi_name'] = nsi_info.get('instance_name')
- candidate['nsi_id'] = nsi_info.get('instance_id')
- candidate['nsi_model_version_id'] = nsi_info.get('model_version_id')
- candidate['nsi_model_invariant_id'] = nsi_info.get('model_invariant_id')
- candidates.append(candidate)
-
+ if response_body is not None:
+ nxi_instances = response_body.get("service-instance", [])
+
+ for nxi_instance in nxi_instances:
+ inventory_attributes = aai_utils.get_inv_values_for_second_level_filter(filtering_attributes,
+ nxi_instance)
+ nxi_info = aai_utils.get_instance_info(nxi_instance)
+ if not filtering_attributes or \
+ self.match_inventory_attributes(filtering_attributes, inventory_attributes,
+ nxi_instance.get('service-instance-id')):
+ if type == 'nssi':
+ profiles = nxi_instance.get('slice-profiles').get('slice-profile')
+ cost = self.conf.data.nssi_candidate_cost
+ elif type == 'nsi':
+ profiles = nxi_instance.get('service-profiles').get('service-profile')
+ cost = self.conf.data.nsi_candidate_cost
+ for profile in profiles:
+ profile_id = profile.get('profile-id')
+ info = Candidate.build_candidate_info('aai', type, cost, candidate_uniqueness, profile_id)
+ profile_info = aai_utils.convert_hyphen_to_under_score(profile)
+ nxi_candidate = NxI(instance_info=nxi_info, profile_info=profile_info, info=info,
+ default_fields=aai_utils.convert_hyphen_to_under_score(default_attributes))
+ candidate = nxi_candidate.convert_nested_dict_to_dict()
+ candidates.append(candidate)
return candidates
-
- def get_nsi_info(self, nsi_link):
- nsi_info = dict()
- if nsi_link:
- nsi_link_path = self._get_aai_path_from_link(nsi_link)
- path = self._aai_versioned_path(nsi_link_path)
- nsi_response = self._request('get', path, data=None)
- if nsi_response and nsi_response.status_code == 200:
- nsi_response_body = nsi_response.json()
- nsi_info['instance_id'] = nsi_response_body.get('service-instance-id')
- nsi_info['instance_name'] = nsi_response_body.get('service-instance-name')
- nsi_info['model_version_id'] = nsi_response_body.get('model-version-id')
- nsi_info['model_invariant_id'] = nsi_response_body.get('model-invariant-id')
-
- return nsi_info
diff --git a/conductor/conductor/data/plugins/inventory_provider/candidates/candidate.py b/conductor/conductor/data/plugins/inventory_provider/candidates/candidate.py
index 7f241c3..2b2eb4b 100644
--- a/conductor/conductor/data/plugins/inventory_provider/candidates/candidate.py
+++ b/conductor/conductor/data/plugins/inventory_provider/candidates/candidate.py
@@ -17,16 +17,17 @@
# -------------------------------------------------------------------------
#
-class Candidate:
+class Candidate(object):
def __init__(self, info):
self.candidate_id = info.get('candidate_id')
if info.get('candidate_type'):
self.candidate_type = info.get('candidate_type')
+ if info.get('service_resource_id'):
+ self.service_resource_id = info.get('service_resource_id')
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()
diff --git a/conductor/conductor/data/plugins/inventory_provider/candidates/nxi_candidate.py b/conductor/conductor/data/plugins/inventory_provider/candidates/nxi_candidate.py
new file mode 100644
index 0000000..5eb75a1
--- /dev/null
+++ b/conductor/conductor/data/plugins/inventory_provider/candidates/nxi_candidate.py
@@ -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 NxI(Candidate):
+ def __init__(self, **kwargs):
+ super().__init__(kwargs['info'])
+ self.nxi_info = kwargs['instance_info']
+ self.profile_info = kwargs['profile_info']
+ self.other = kwargs['default_fields']
diff --git a/conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py b/conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py
new file mode 100644
index 0000000..2fbaabd
--- /dev/null
+++ b/conductor/conductor/data/plugins/inventory_provider/utils/aai_utils.py
@@ -0,0 +1,81 @@
+#
+# -------------------------------------------------------------------------
+# 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.
+#
+# -------------------------------------------------------------------------
+#
+
+QUERY_PARAMS = {'service_instance': ["service-instance-id", "service-instance-name", "environment-context",
+ "workload-context", "model-invariant-id", "model-version-id", "widget-model-id",
+ "widget-model-version", "service-instance-location-id", "orchestration-status"]
+ }
+
+
+def convert_hyphen_to_under_score(hyphened_dict):
+ converted_dict = dict()
+ if hyphened_dict:
+ for key in hyphened_dict:
+ if '-' in key:
+ converted_dict[key.replace('-', '_')] = hyphened_dict[key]
+ else:
+ converted_dict[key] = hyphened_dict[key]
+ if 'resource_version' in converted_dict:
+ converted_dict.pop('resource_version')
+ return converted_dict
+
+
+def add_query_params(filtering_attributes):
+ if not filtering_attributes:
+ return ''
+ url = '?'
+ for key, value in filtering_attributes.items():
+ url = f'{url}{key}={value}&'
+ return url
+
+
+def add_query_params_and_depth(filtering_attributes, depth):
+ url_with_query_params = add_query_params(filtering_attributes)
+ if url_with_query_params:
+ return f"{url_with_query_params}depth={depth}"
+ else:
+ return f"?depth={depth}"
+
+
+def get_first_level_and_second_level_filter(filtering_attributes, aai_node):
+ second_level_filters = dict()
+ valid_query_params = QUERY_PARAMS.get(aai_node)
+ for key in list(filtering_attributes):
+ if key not in valid_query_params:
+ second_level_filters[key] = filtering_attributes[key]
+ del filtering_attributes[key]
+ return second_level_filters
+
+
+def get_inv_values_for_second_level_filter(second_level_filters, nssi_instance):
+ if not second_level_filters:
+ return None
+ inventory_attributes = dict()
+ for key in list(second_level_filters):
+ inventory_attributes[key] = nssi_instance.get(key)
+ return inventory_attributes
+
+
+def get_instance_info(nxi_instance):
+ nxi_dict = dict()
+ nxi_dict['instance_id'] = nxi_instance.get('service-instance-id')
+ nxi_dict['instance_name'] = nxi_instance.get('service-instance-name')
+ if nxi_instance.get('service-function'):
+ nxi_dict['domain'] = nxi_instance.get('service-function')
+ return nxi_dict
diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/first_level_filter.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/first_level_filter.json
new file mode 100644
index 0000000..be1810c
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/first_level_filter.json
@@ -0,0 +1,5 @@
+{"orchestration-status": "active",
+"model-invariant-id": "123644",
+"model-version-id": "524846",
+"environment-context": "shared"
+}
diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_candidate.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_candidate.json
index 28a4f8f..b5a4a7d 100644
--- a/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_candidate.json
+++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_candidate.json
@@ -1,36 +1,30 @@
[
{
- "exp_data_rate":0,
- "conn_density":0,
- "coverage_area_ta_list":"[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
- "activity_factor":0,
- "cs_availability":null,
- "candidate_id":"1a636c4d-5e76-427e-bfd6-241a947224b0",
- "area_traffic_cap_dl":null,
- "latency":20,
- "service_area_dimension":null,
- "domain":"cn",
- "e2e_latency":0,
- "area_traffic_cap_ul":null,
- "inventory_provider":"aai",
- "exp_data_rate_ul":100,
- "exp_data_rate_dl":100,
- "max_number_of_ues":0,
- "ue_mobility_level":"stationary",
- "candidate_type":"nssi",
- "traffic_density":0,
- "payload_size":0,
- "jitter":0,
- "survival_time":0,
- "resource_sharing_level":"0",
+ "candidate_id":"cdad9f49-4201-4e3a-aac1-b0f27902c299",
"inventory_type":"nssi",
- "reliability":null,
+ "uniqueness": "true",
"cost":1.0,
- "nsi_name": "nsi_test_0211",
- "nsi_id": "4115d3c8-dd59-45d6-b09d-e756dee9b518",
- "nsi_model_version_id": "8b664b11-6646-4776-9f59-5c3de46da2d6",
- "nsi_model_invariant_id": "39b10fe6-efcc-40bc-8184-c38414b80771",
+ "inventory_provider": "aai",
"instance_name": "nssi_test_0211",
- "uniqueness": "true"
+ "instance_id": "1a636c4d-5e76-427e-bfd6-241a947224b0",
+ "domain": "cn",
+ "creation_cost": 1,
+
+ "profile_id": "cdad9f49-4201-4e3a-aac1-b0f27902c299",
+ "latency": 20,
+ "max_number_of_UEs": 0,
+ "coverage_area_TA_list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+ "ue_mobility_level": "stationary",
+ "resource_sharing_level": "0",
+ "exp_data_rate_UL": 100,
+ "exp_data_rate_DL": 100,
+ "activity_factor": 0,
+ "e2e_latency": 0,
+ "jitter": 0,
+ "survival_time": 0,
+ "exp_data_rate": 0,
+ "payload_size": 0,
+ "traffic_density": 0,
+ "conn_density": 0
}
]
diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_demand_list.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_demand_list.json
new file mode 100644
index 0000000..15168be
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_demand_list.json
@@ -0,0 +1,17 @@
+{
+ "embb_cn": [{
+ "inventory_provider": "aai",
+ "inventory_type": "nssi",
+ "unique": "true",
+ "filtering_attributes": {
+ "model-version-id": "bfba363e-e39c-4bd9-a9d5-1371c28f4d22",
+ "model-invariant-id": "21d57d4b-52ad-4d3c-a798-248b5bb9124a",
+ "orchestration-status": "active",
+ "service-role": "nssi",
+ "environment-context": "shared"
+ },
+ "default_attributes": {
+ "creation-cost": 1
+ }
+ }]
+}
diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_response.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_response.json
index e22ce39..286c8bf 100644
--- a/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_response.json
+++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/nssi_response.json
@@ -3,7 +3,8 @@
"service-instance-name": "nssi_test_0211",
"service-type": "embb",
"service-role": "nssi",
- "environment-context": "cn",
+ "service-function": "cn",
+ "environment-context": "shared",
"model-invariant-id": "21d57d4b-52ad-4d3c-a798-248b5bb9124a",
"model-version-id": "bfba363e-e39c-4bd9-a9d5-1371c28f4d22",
"resource-version": "1581418601616",
diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/slice_profile.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/slice_profile.json
new file mode 100644
index 0000000..4bbda28
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/slice_profile.json
@@ -0,0 +1,19 @@
+{
+ "profile-id": "cdad9f49-4201-4e3a-aac1-b0f27902c299",
+ "latency": 20,
+ "max-number-of-UEs": 0,
+ "coverage-area-TA-list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+ "ue-mobility-level": "stationary",
+ "resource-sharing-level": "0",
+ "exp-data-rate-UL": 100,
+ "exp-data-rate-DL": 100,
+ "activity-factor": 0,
+ "e2e-latency": 0,
+ "jitter": 0,
+ "survival-time": 0,
+ "exp-data-rate": 0,
+ "payload-size": 0,
+ "traffic-density": 0,
+ "conn-density": 0,
+ "resource-version": "1581418602494"
+}
diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/slice_profile_converted.json b/conductor/conductor/tests/unit/data/plugins/inventory_provider/slice_profile_converted.json
new file mode 100644
index 0000000..f7581a2
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/slice_profile_converted.json
@@ -0,0 +1,18 @@
+{
+ "profile_id": "cdad9f49-4201-4e3a-aac1-b0f27902c299",
+ "latency": 20,
+ "max_number_of_UEs": 0,
+ "coverage_area_TA_list": "[{\"province\":\"??\",\"city\":\"???\",\"county\":\"???\",\"street\":\"?????\"}]",
+ "ue_mobility_level": "stationary",
+ "resource_sharing_level": "0",
+ "exp_data_rate_UL": 100,
+ "exp_data_rate_DL": 100,
+ "activity_factor": 0,
+ "e2e_latency": 0,
+ "jitter": 0,
+ "survival_time": 0,
+ "exp_data_rate": 0,
+ "payload_size": 0,
+ "traffic_density": 0,
+ "conn_density": 0
+}
diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py b/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py
index 55bdef9..4c38feb 100644
--- a/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py
+++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai.py
@@ -725,44 +725,60 @@ tenant/3c6c471ada7747fe8ff7f28e100b61e8/vservers/vserver/00bddefc-126e-4e4f-a18d
self.assertEqual(None, self.aai_ep.match_hpa(candidate_json['candidate_list'][1],
feature_json[5]))
- def test_get_nssi_candidates(self):
+ def test_filter_nssi_candidates(self):
nssi_response_file = './conductor/tests/unit/data/plugins/inventory_provider/nssi_response.json'
nssi_response = json.loads(open(nssi_response_file).read())
nssi_candidates_file = './conductor/tests/unit/data/plugins/inventory_provider/nssi_candidate.json'
nssi_candidates = json.loads(open(nssi_candidates_file).read())
- nsi_info = {'instance_name': 'nsi_test_0211',
- 'instance_id': '4115d3c8-dd59-45d6-b09d-e756dee9b518',
- 'model_version_id': '8b664b11-6646-4776-9f59-5c3de46da2d6',
- 'model_invariant_id': '39b10fe6-efcc-40bc-8184-c38414b80771'}
+ service_role = 'nssi'
+ second_level_filter = dict()
+ second_level_filter['service-role'] = service_role
+ default_attributes = dict()
+ default_attributes['creation_cost'] =1
+ self.assertEqual(nssi_candidates, self.aai_ep.filter_nxi_candidates(nssi_response, second_level_filter,
+ default_attributes, "true", service_role))
- self.nsi_patcher = mock.patch('conductor.data.plugins.inventory_provider.aai.AAI.get_nsi_info',
- return_value=nsi_info)
- self.nsi_patcher.start()
+ nssi_response['service-instance'][0]['service-role'] = 'service'
- service_role = 'nssi'
- model_invariant_id = '21d57d4b-52ad-4d3c-a798-248b5bb9124a'
- model_version_id = 'bfba363e-e39c-4bd9-a9d5-1371c28f4d22'
- orchestration_status = 'active'
- filtering_attributes = dict()
- filtering_attributes['orchestration-status'] = orchestration_status
- filtering_attributes['service-role'] = service_role
- filtering_attributes['model-invariant-id'] = model_invariant_id
- filtering_attributes['model-version-id'] = model_version_id
+ self.assertEqual([], self.aai_ep.filter_nxi_candidates(nssi_response, second_level_filter, default_attributes,
+ "true", service_role))
- self.assertEqual(nssi_candidates, self.aai_ep.filter_nssi_candidates(nssi_response, filtering_attributes, "true"))
+ self.assertEqual([], self.aai_ep.filter_nxi_candidates(None, second_level_filter, default_attributes,
+ "true", service_role))
- nssi_response['service-instance'][0]['orchestration-status'] = 'deactivated'
+ self.assertEqual([], self.aai_ep.filter_nxi_candidates(None, None, default_attributes, "true", service_role))
- self.assertEqual([], self.aai_ep.filter_nssi_candidates(nssi_response, filtering_attributes, "true"))
+ self.assertEqual(nssi_candidates, self.aai_ep.filter_nxi_candidates(nssi_response, None, default_attributes,
+ "true", service_role))
+ del nssi_candidates[0]['creation_cost']
+ self.assertEqual(nssi_candidates, self.aai_ep.filter_nxi_candidates(nssi_response, None, None, "true",
+ service_role))
- nssi_response['service-instance'][0]['service-role'] = 'service'
+ def test_resolve_demands_inventory_type_nssi(self):
+ self.aai_ep.conf.HPA_enabled = True
+ TraigeTranslator.getPlanIdNAme = mock.MagicMock(return_value=None)
+ TraigeTranslator.addDemandsTriageTranslator = mock.MagicMock(return_value=None)
- self.assertEqual([], self.aai_ep.filter_nssi_candidates(nssi_response, filtering_attributes, "true"))
+ plan_info = {
+ 'plan_name': 'name',
+ 'plan_id': 'id'
+ }
+ triage_translator_data = None
- self.assertEqual([], self.aai_ep.filter_nssi_candidates(None, filtering_attributes, "true"))
+ demands_list_file = './conductor/tests/unit/data/plugins/inventory_provider/nssi_demand_list.json'
+ demands_list = json.loads(open(demands_list_file).read())
- self.assertEqual([], self.aai_ep.filter_nssi_candidates(None, None, "true"))
+ nssi_response_file = './conductor/tests/unit/data/plugins/inventory_provider/nssi_response.json'
+ nssi_response = json.loads(open(nssi_response_file).read())
+ nssi_candidates_file = './conductor/tests/unit/data/plugins/inventory_provider/nssi_candidate.json'
+ nssi_candidates = json.loads(open(nssi_candidates_file).read())
+ result = dict()
+ result['embb_cn'] = nssi_candidates
- self.assertEqual([], self.aai_ep.filter_nssi_candidates(nssi_response, None, "true"))
+ self.mock_get_nxi_candidates = mock.patch.object(AAI, 'get_nxi_candidates',
+ return_value=nssi_response)
+ self.mock_get_nxi_candidates.start()
+ self.assertEqual(result, self.aai_ep.resolve_demands(demands_list, plan_info=plan_info,
+ triage_translator_data=triage_translator_data))
diff --git a/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai_utils.py b/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai_utils.py
new file mode 100644
index 0000000..23b6640
--- /dev/null
+++ b/conductor/conductor/tests/unit/data/plugins/inventory_provider/test_aai_utils.py
@@ -0,0 +1,95 @@
+#
+# -------------------------------------------------------------------------
+# 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.
+#
+# -------------------------------------------------------------------------
+#
+
+import unittest
+import json
+
+from mock import patch
+
+from conductor.data.plugins.inventory_provider.utils import aai_utils
+
+class TestUtils(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ patch.stopall()
+
+ def test_convert_hyphen_to_under_score(self):
+ slice_profile_file = './conductor/tests/unit/data/plugins/inventory_provider/slice_profile.json'
+ slice_profile_hyphened = json.loads(open(slice_profile_file).read())
+
+ converted_slice_profile_file = './conductor/tests/unit/data/plugins/inventory_provider' \
+ '/slice_profile_converted.json'
+ converted_slice_profile = json.loads(open(converted_slice_profile_file).read())
+ self.assertEqual(converted_slice_profile, aai_utils.convert_hyphen_to_under_score(slice_profile_hyphened))
+
+ def test_get_first_level_and_second_level_filter(self):
+ first_level_filter_file = './conductor/tests/unit/data/plugins/inventory_provider/first_level_filter.json'
+ first_level_filter = json.loads(open(first_level_filter_file).read())
+ filtering_attributes = dict()
+ filtering_attributes['orchestration-status'] = "active"
+ filtering_attributes['service-role'] = "nssi"
+ filtering_attributes['model-invariant-id'] = "123644"
+ filtering_attributes['model-version-id'] = "524846"
+ filtering_attributes['environment-context'] = 'shared'
+
+ second_level_filter = {'service-role': 'nssi'}
+
+ self.assertEqual(second_level_filter, aai_utils.get_first_level_and_second_level_filter(filtering_attributes,
+ "service_instance"))
+
+ self.assertEqual(first_level_filter, filtering_attributes)
+
+ self.assertEqual({}, aai_utils.get_first_level_and_second_level_filter(filtering_attributes,
+ "service_instance"))
+
+ def test_add_query_params(self):
+ first_level_filter_file = './conductor/tests/unit/data/plugins/inventory_provider/first_level_filter.json'
+ first_level_filter = json.loads(open(first_level_filter_file).read())
+
+ query_params = "?orchestration-status=active&model-invariant-id=123644&model-version-id=524846&" \
+ "environment-context=shared&"
+
+ self.assertEqual(query_params, aai_utils.add_query_params(first_level_filter))
+ first_level_filter = {}
+ self.assertEqual('', aai_utils.add_query_params(first_level_filter))
+
+ def test_add_query_params_and_depth(self):
+ first_level_filter_file = './conductor/tests/unit/data/plugins/inventory_provider/first_level_filter.json'
+ first_level_filter = json.loads(open(first_level_filter_file).read())
+
+ query_params_with_depth = "?orchestration-status=active&model-invariant-id=123644&model-version-id=524846&" \
+ "environment-context=shared&depth=2"
+
+ self.assertEqual(query_params_with_depth, aai_utils.add_query_params_and_depth(first_level_filter, "2"))
+
+ only_depth = "?depth=2"
+ first_level_filter = {}
+ self.assertEqual(only_depth, aai_utils.add_query_params_and_depth(first_level_filter, "2"))
+
+ def test_get_inv_values_for_second_level_filter(self):
+ nssi_response_file = './conductor/tests/unit/data/plugins/inventory_provider/nssi_response.json'
+ nssi_response = json.loads(open(nssi_response_file).read())
+ nssi_instance = nssi_response.get("service-instance")[0]
+ second_level_filter = {'service-role': 'nsi'}
+ inventory_attribute = {'service-role': 'nssi'}
+ self.assertEqual(inventory_attribute, aai_utils.get_inv_values_for_second_level_filter(second_level_filter,
+ nssi_instance))
diff --git a/conductor/conductor/tests/unit/solver/optimizer/constraints/test_threshold.py b/conductor/conductor/tests/unit/solver/optimizer/constraints/test_threshold.py
index 311e790..f12ed9c 100644
--- a/conductor/conductor/tests/unit/solver/optimizer/constraints/test_threshold.py
+++ b/conductor/conductor/tests/unit/solver/optimizer/constraints/test_threshold.py
@@ -33,7 +33,7 @@ class TestThreshold(unittest.TestCase):
# test 1
properties = {'evaluate':
[{'attribute': 'latency', 'threshold': 30, 'operator': 'lte'},
- {'attribute': 'exp_data_rate_ul', 'threshold': 70, 'operator': 'gte'}]}
+ {'attribute': 'exp_data_rate_UL', 'threshold': 70, 'operator': 'gte'}]}
threshold_obj = Threshold('urllc_threshold', 'threshold', ['URLLC'], _priority=0,
_properties=properties)
diff --git a/conductor/tox.ini b/conductor/tox.ini
index 3fe867a..b65abd4 100644
--- a/conductor/tox.ini
+++ b/conductor/tox.ini
@@ -64,8 +64,8 @@ select = E,H,W,F
max-line-length = 119
exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,install-guide,*/tests/*
show-source = True
-ignore=
-per-file-ignores=
+ignore= W503 #conflict with W504
+per-file-ignores= conductor/data/plugins/inventory_provider/aai.py:F821
[hacking]
import_exceptions = conductor.common.i18n