aboutsummaryrefslogtreecommitdiffstats
path: root/app/discover/clique_finder.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/discover/clique_finder.py')
-rw-r--r--app/discover/clique_finder.py138
1 files changed, 76 insertions, 62 deletions
diff --git a/app/discover/clique_finder.py b/app/discover/clique_finder.py
index 57b2e3b..4d68eb4 100644
--- a/app/discover/clique_finder.py
+++ b/app/discover/clique_finder.py
@@ -48,61 +48,53 @@ class CliqueFinder(Fetcher):
self.find_cliques_for_type(clique_type)
self.log.info("finished scanning for cliques")
- # Calculate priority score
+ # Calculate priority score for clique type per environment and configuration
def _get_priority_score(self, clique_type):
+ # environment-specific clique type takes precedence
if self.env == clique_type['environment']:
+ return 16
+ if (self.env_config['distribution'] == clique_type.get('distribution')
+ and
+ self.env_config['distribution_version'] ==
+ clique_type.get('distribution_version')):
+ return 8
+ if clique_type.get('mechanism_drivers') \
+ in self.env_config['mechanism_drivers']:
return 4
- if (self.env_config['distribution'] == clique_type.get('distribution') and
- self.env_config['distribution_version'] == clique_type.get('distribution_version')):
- return 3
- if clique_type.get('mechanism_drivers') in self.env_config['mechanism_drivers']:
- return 2
if self.env_config['type_drivers'] == clique_type.get('type_drivers'):
+ return 2
+ if clique_type.get('environment', '') == 'ANY':
+ # environment=ANY serves as fallback option, but it's not mandatory
return 1
else:
return 0
# Get clique type with max priority
- # for given environment configuration and focal point type
- def _get_clique_type(self, focal_point, clique_types):
- # If there's no configuration match for the specified environment,
- # we use the default clique type definition with environment='ANY'
- fallback_type = next(
- filter(lambda t: t['environment'] == 'ANY', clique_types),
- None
- )
- if not fallback_type:
- raise ValueError("No fallback clique type (ANY) "
- "defined for focal point type '{}'"
- .format(focal_point))
-
- clique_types.remove(fallback_type)
-
- priority_scores = [self._get_priority_score(clique_type)
- for clique_type
- in clique_types]
- max_score = max(priority_scores) if priority_scores else 0
-
- return (fallback_type
- if max_score == 0
- else clique_types[priority_scores.index(max_score)])
+ # for given focal point type
+ def _get_clique_type(self, clique_types):
+ scored_clique_types = [{'score': self._get_priority_score(clique_type),
+ 'clique_type': clique_type}
+ for clique_type in clique_types]
+ max_score = max(scored_clique_types, key=lambda t: t['score'])
+ if max_score['score'] == 0:
+ self.log.warn('No matching clique types for focal point type: {}'
+ .format(clique_types[0].get('focal_point_type')))
+ return None
+ return max_score.get('clique_type')
def get_clique_types(self):
if not self.clique_types_by_type:
- clique_types_by_focal_point = self.clique_types.aggregate([{
- "$group": {
- "_id": "$focal_point_type",
- "types": {"$push": "$$ROOT"}
- }
- }])
-
- self.clique_types_by_type = {
- cliques['_id']: self._get_clique_type(cliques['_id'],
- cliques['types'])
- for cliques in
- clique_types_by_focal_point
- }
-
+ clique_types_candidates = {}
+ for clique in self.clique_types.find({}):
+ fp_type = clique.get('focal_point_type', '')
+ if not clique_types_candidates.get(fp_type):
+ clique_types_candidates[fp_type] = []
+ clique_types_candidates[fp_type].append(clique)
+ for t in clique_types_candidates.keys():
+ selected = self._get_clique_type(clique_types_candidates[t])
+ if not selected:
+ continue
+ self.clique_types_by_type[t] = selected
return self.clique_types_by_type
def find_cliques_for_type(self, clique_type):
@@ -125,11 +117,14 @@ class CliqueFinder(Fetcher):
.find_one({"focal_point_type": o['type']})
constraints = [] if not constraint else constraint["constraints"]
clique_types = self.get_clique_types()
- clique_type = clique_types[o['type']]
- new_clique = self.construct_clique_for_focal_point(o, clique_type,
- constraints)
- if not new_clique:
+ clique_type = clique_types.get(o['type'])
+ if not clique_type:
self.cliques.delete({'_id': clique['_id']})
+ else:
+ new_clique = self.construct_clique_for_focal_point(o, clique_type,
+ constraints)
+ if not new_clique:
+ self.cliques.delete({'_id': clique['_id']})
def construct_clique_for_focal_point(self, o, clique_type, constraints):
# keep a hash of nodes in clique that were visited for each type
@@ -146,12 +141,15 @@ class CliqueFinder(Fetcher):
for c in constraints:
val = o[c] if c in o else None
clique["constraints"][c] = val
+ allow_implicit = clique_type.get('use_implicit_links', False)
for link_type in clique_type["link_types"]:
- self.check_link_type(clique, link_type, nodes_of_type)
+ self.check_link_type(clique, link_type, nodes_of_type,
+ allow_implicit=allow_implicit)
# after adding the links to the clique, create/update the clique
if not clique["links"]:
return None
+ clique["clique_type"] = clique_type["_id"]
focal_point_obj = self.inventory.find({"_id": clique["focal_point"]})
if not focal_point_obj:
return None
@@ -198,25 +196,32 @@ class CliqueFinder(Fetcher):
'-'.join(link_type_parts)
return CliqueFinder.link_type_reversed.get(link_type)
- def check_link_type(self, clique, link_type, nodes_of_type):
+ def check_link_type(self, clique, link_type, nodes_of_type,
+ allow_implicit=False):
# check if it's backwards
link_type_reversed = self.get_link_type_reversed(link_type)
# handle case of links like T<-->T
self_linked = link_type == link_type_reversed
use_reversed = False
if not self_linked:
- matches = self.links.find_one({
+ link_search_condition = {
"environment": self.env,
"link_type": link_type_reversed
- })
+ }
+ if not allow_implicit:
+ link_search_condition['implicit'] = False
+ matches = self.links.find_one(link_search_condition)
use_reversed = True if matches else False
if self_linked or not use_reversed:
- self.check_link_type_forward(clique, link_type, nodes_of_type)
+ self.check_link_type_forward(clique, link_type, nodes_of_type,
+ allow_implicit=allow_implicit)
if self_linked or use_reversed:
- self.check_link_type_back(clique, link_type, nodes_of_type)
+ self.check_link_type_back(clique, link_type, nodes_of_type,
+ allow_implicit=allow_implicit)
def check_link_type_for_direction(self, clique, link_type, nodes_of_type,
- is_reversed=False):
+ is_reversed=False,
+ allow_implicit=False):
if is_reversed:
link_type = self.get_link_type_reversed(link_type)
from_type = link_type[:link_type.index("-")]
@@ -233,7 +238,8 @@ class CliqueFinder(Fetcher):
clique,
link_type,
side_to_match,
- other_side)
+ other_side,
+ allow_implicit=allow_implicit)
nodes_to_add = nodes_to_add | matches
if other_side_type not in nodes_of_type:
nodes_of_type[other_side_type] = set()
@@ -241,13 +247,17 @@ class CliqueFinder(Fetcher):
nodes_of_type[other_side_type] | nodes_to_add
def find_matches_for_point(self, match_point, clique, link_type,
- side_to_match, other_side) -> set:
+ side_to_match, other_side,
+ allow_implicit=False) -> set:
nodes_to_add = set()
- matches = self.links.find({
+ link_search_condition = {
"environment": self.env,
"link_type": link_type,
side_to_match: ObjectId(match_point)
- })
+ }
+ if not allow_implicit:
+ link_search_condition['implicit'] = False
+ matches = self.links.find(link_search_condition)
for link in matches:
link_id = link["_id"]
if link_id in clique["links"]:
@@ -260,10 +270,14 @@ class CliqueFinder(Fetcher):
nodes_to_add.add(other_side_point)
return nodes_to_add
- def check_link_type_forward(self, clique, link_type, nodes_of_type):
+ def check_link_type_forward(self, clique, link_type, nodes_of_type,
+ allow_implicit=False):
self.check_link_type_for_direction(clique, link_type, nodes_of_type,
- is_reversed=False)
+ is_reversed=False,
+ allow_implicit=allow_implicit)
- def check_link_type_back(self, clique, link_type, nodes_of_type):
+ def check_link_type_back(self, clique, link_type, nodes_of_type,
+ allow_implicit=False):
self.check_link_type_for_direction(clique, link_type, nodes_of_type,
- is_reversed=True)
+ is_reversed=True,
+ allow_implicit=allow_implicit)