valet/valet/engine/optimizer/app_manager/app_topology_parser.py
Tin Lam 67f6434bc0 Enable PEP8 check and correct all PEP8 issues
This patch set enables PEP8 standard check for project Valet, and
corrects all outstanding PEP8 issues.

Story: #2001040
Task: #4602

Co-Authored-By: Omar Rivera <gomarivera@gmail.com>
Change-Id: I4b987ff28b02ea8a6da77fb0f29eda1515d212ac
2017-05-28 16:18:17 +00:00

462 lines
21 KiB
Python
Executable File

#
# Copyright 2014-2017 AT&T Intellectual Property
#
# 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.
"""App Topology Parser.
- Restrictions of nested groups: EX in EX, EX in DIV, DIV in EX, DIV in DIV
- VM/group cannot exist in multiple EX groups
- Nested group's level cannot be higher than nesting group
- No supporting the following Heat components
OS::Nova::ServerGroup
OS::Heat::AutoScalingGroup
OS::Heat::Stack
OS::Heat::ResourceGroup
OS::Heat::ResourceGroup
"""
import six
from valet.engine.optimizer.app_manager.app_topology_base import LEVELS
from valet.engine.optimizer.app_manager.app_topology_base import VGroup
from valet.engine.optimizer.app_manager.app_topology_base import VM
class Parser(object):
"""Parser Class.
This class handles parsing out the data related to the desired
topology from a template.
not supported OS::Nova::ServerGroup OS::Heat::AutoScalingGroup
OS::Heat::Stack OS::Heat::ResourceGroup
"""
def __init__(self, _high_level_allowed, _logger):
"""Init Parser Class."""
self.logger = _logger
self.high_level_allowed = _high_level_allowed
self.format_version = None
self.stack_id = None # used as application id
self.application_name = None
self.action = None # [create|update|ping]
self.candidate_list_map = {}
self.status = "success"
def set_topology(self, _graph):
"""Return result of set_topology which parses input to get topology."""
if "version" in _graph.keys():
self.format_version = _graph["version"]
else:
self.format_version = "0.0"
if "stack_id" in _graph.keys():
self.stack_id = _graph["stack_id"]
else:
self.stack_id = "none"
if "application_name" in _graph.keys():
self.application_name = _graph["application_name"]
else:
self.application_name = "none"
if "action" in _graph.keys():
self.action = _graph["action"]
else:
self.action = "any"
if "locations" in _graph.keys() and len(_graph["locations"]) > 0:
if len(_graph["resources"]) == 1:
v_uuid = _graph["resources"].keys()[0]
self.candidate_list_map[v_uuid] = _graph["locations"]
return self._set_topology(_graph["resources"])
def _set_topology(self, _elements):
vgroups = {}
vms = {}
for rk, r in _elements.iteritems():
if r["type"] == "OS::Nova::Server":
vm = VM(self.stack_id, rk)
if "name" in r.keys():
vm.name = r["name"]
else:
vm.name = vm.uuid
flavor_id = r["properties"]["flavor"]
if isinstance(flavor_id, six.string_types):
vm.flavor = flavor_id
else:
vm.flavor = str(flavor_id)
if "availability_zone" in r["properties"].keys():
az = r["properties"]["availability_zone"]
# NOTE: do not allow to specify a certain host name
vm.availability_zone = az.split(":")[0]
if "locations" in r.keys():
if len(r["locations"]) > 0:
self.candidate_list_map[rk] = r["locations"]
vms[vm.uuid] = vm
self.logger.info("vm = " + vm.uuid)
elif r["type"] == "OS::Cinder::Volume":
self.logger.warn("Parser: do nothing for volume at this "
"version")
elif r["type"] == "ATT::Valet::GroupAssignment":
vgroup = VGroup(self.stack_id, rk)
vgroup.vgroup_type = None
if "group_type" in r["properties"].keys():
if r["properties"]["group_type"] == "affinity":
vgroup.vgroup_type = "AFF"
elif r["properties"]["group_type"] == "diversity":
vgroup.vgroup_type = "DIV"
elif r["properties"]["group_type"] == "exclusivity":
vgroup.vgroup_type = "EX"
else:
self.status = "unknown group = " + \
r["properties"]["group_type"]
return {}, {}
else:
self.status = "no group type"
return {}, {}
if "group_name" in r["properties"].keys():
vgroup.name = r["properties"]["group_name"]
else:
if vgroup.vgroup_type == "EX":
self.status = "no exclusivity group identifier"
return {}, {}
else:
vgroup.name = "any"
if "level" in r["properties"].keys():
vgroup.level = r["properties"]["level"]
if vgroup.level != "host":
if self.high_level_allowed is False:
self.status = ("only host level of affinity group "
"allowed due to the mis-match of "
"host naming convention")
return {}, {}
else:
self.status = "no grouping level"
return {}, {}
vgroups[vgroup.uuid] = vgroup
msg = "group = %s, type = %s"
self.logger.info(msg % (vgroup.name, vgroup.vgroup_type))
if self._merge_diversity_groups(_elements, vgroups, vms) is False:
return {}, {}
if self._merge_exclusivity_groups(_elements, vgroups, vms) is False:
return {}, {}
if self._merge_affinity_groups(_elements, vgroups, vms) is False:
return {}, {}
""" delete all EX and DIV vgroups after merging """
for vgk in vgroups.keys():
vg = vgroups[vgk]
if vg.vgroup_type == "DIV" or vg.vgroup_type == "EX":
del vgroups[vgk]
return vgroups, vms
def _merge_diversity_groups(self, _elements, _vgroups, _vms):
for level in LEVELS:
for rk, r in _elements.iteritems():
if r["type"] == "ATT::Valet::GroupAssignment" and \
r["properties"]["group_type"] == "diversity" and \
r["properties"]["level"] == level:
vgroup = _vgroups[rk]
for vk in r["properties"]["resources"]:
if vk in _vms.keys():
vgroup.subvgroups[vk] = _vms[vk]
_vms[vk].diversity_groups[rk] = (
vgroup.level + ":" + vgroup.name)
elif vk in _vgroups.keys():
vg = _vgroups[vk]
if LEVELS.index(vg.level) > LEVELS.index(level):
self.status = ("grouping scope: nested "
"group's level is higher")
return False
if (vg.vgroup_type == "DIV" or
vg.vgroup_type == "EX"):
msg = ("{0} not allowd to be nested in "
"diversity group")
self.status = msg.format(vg.vgroup_type)
return False
vgroup.subvgroups[vk] = vg
vg.diversity_groups[rk] = vgroup.level + ":" + \
vgroup.name
else:
self.status = "invalid resource = " + vk
return False
return True
def _merge_exclusivity_groups(self, _elements, _vgroups, _vms):
for level in LEVELS:
for rk, r in _elements.iteritems():
if r["type"] == "ATT::Valet::GroupAssignment" and \
r["properties"]["group_type"] == "exclusivity" and \
r["properties"]["level"] == level:
vgroup = _vgroups[rk]
for vk in r["properties"]["resources"]:
if vk in _vms.keys():
vgroup.subvgroups[vk] = _vms[vk]
_vms[vk].exclusivity_groups[rk] = (
vgroup.level + ":" + vgroup.name)
elif vk in _vgroups.keys():
vg = _vgroups[vk]
if LEVELS.index(vg.level) > LEVELS.index(level):
self.status = "grouping scope: nested " \
"group's level is higher"
return False
if (vg.vgroup_type == "DIV" or
vg.vgroup_type == "EX"):
msg = ("{0}) not allowd to be nested in "
"exclusivity group")
self.status = msg.format(vg.vgroup_type)
return False
vgroup.subvgroups[vk] = vg
vg.exclusivity_groups[rk] = vgroup.level + ":" + \
vgroup.name
else:
self.status = "invalid resource = " + vk
return False
return True
def _merge_affinity_groups(self, _elements, _vgroups, _vms):
# key is uuid of vm or vgroup & value is its parent vgroup
affinity_map = {}
for level in LEVELS:
for rk, r in _elements.iteritems():
if r["type"] == "ATT::Valet::GroupAssignment" and \
r["properties"]["group_type"] == "affinity" and \
r["properties"]["level"] == level:
vgroup = None
if rk in _vgroups.keys():
vgroup = _vgroups[rk]
else:
continue
for vk in r["properties"]["resources"]:
if vk in _vms.keys():
vgroup.subvgroups[vk] = _vms[vk]
_vms[vk].survgroup = vgroup
affinity_map[vk] = vgroup
self._add_implicit_diversity_groups(
vgroup, _vms[vk].diversity_groups)
self._add_implicit_exclusivity_groups(
vgroup, _vms[vk].exclusivity_groups)
self._add_memberships(vgroup, _vms[vk])
del _vms[vk]
elif vk in _vgroups.keys():
vg = _vgroups[vk]
if LEVELS.index(vg.level) > LEVELS.index(level):
self.status = ("grouping scope: nested "
"group's level is higher")
return False
if (vg.vgroup_type == "DIV" or
vg.vgroup_type == "EX"):
if not self._merge_subgroups(
vgroup, vg.subvgroups, _vms, _vgroups,
_elements, affinity_map):
return False
del _vgroups[vk]
else:
if not self._exist_in_subgroups(vk, vgroup):
if not self._get_subgroups(
vg, _elements, _vgroups, _vms,
affinity_map):
return False
vgroup.subvgroups[vk] = vg
vg.survgroup = vgroup
affinity_map[vk] = vgroup
self._add_implicit_diversity_groups(
vgroup, vg.diversity_groups)
self._add_implicit_exclusivity_groups(
vgroup, vg.exclusivity_groups)
self._add_memberships(vgroup, vg)
del _vgroups[vk]
else:
# vk belongs to the other vgroup already
# or refer to invalid resource
if vk not in affinity_map.keys():
self.status = "invalid resource = " + vk
return False
if affinity_map[vk].uuid != vgroup.uuid:
if not self._exist_in_subgroups(vk, vgroup):
self._set_implicit_grouping(
vk, vgroup, affinity_map, _vgroups)
return True
def _merge_subgroups(self, _vgroup, _subgroups, _vms, _vgroups,
_elements, _affinity_map):
for vk, _ in _subgroups.iteritems():
if vk in _vms.keys():
_vgroup.subvgroups[vk] = _vms[vk]
_vms[vk].survgroup = _vgroup
_affinity_map[vk] = _vgroup
self._add_implicit_diversity_groups(
_vgroup, _vms[vk].diversity_groups)
self._add_implicit_exclusivity_groups(
_vgroup, _vms[vk].exclusivity_groups)
self._add_memberships(_vgroup, _vms[vk])
del _vms[vk]
elif vk in _vgroups.keys():
vg = _vgroups[vk]
if LEVELS.index(vg.level) > LEVELS.index(_vgroup.level):
self.status = ("grouping scope: nested group's level is "
"higher")
return False
if vg.vgroup_type == "DIV" or vg.vgroup_type == "EX":
if not self._merge_subgroups(_vgroup, vg.subvgroups,
_vms, _vgroups,
_elements, _affinity_map):
return False
del _vgroups[vk]
else:
if self._exist_in_subgroups(vk, _vgroup) is None:
if not self._get_subgroups(vg, _elements, _vgroups,
_vms, _affinity_map):
return False
_vgroup.subvgroups[vk] = vg
vg.survgroup = _vgroup
_affinity_map[vk] = _vgroup
self._add_implicit_diversity_groups(
_vgroup, vg.diversity_groups)
self._add_implicit_exclusivity_groups(
_vgroup, vg.exclusivity_groups)
self._add_memberships(_vgroup, vg)
del _vgroups[vk]
else:
# vk belongs to the other vgroup already
# or refer to invalid resource
if vk not in _affinity_map.keys():
self.status = "invalid resource = " + vk
return False
if _affinity_map[vk].uuid != _vgroup.uuid:
if self._exist_in_subgroups(vk, _vgroup) is None:
self._set_implicit_grouping(
vk, _vgroup, _affinity_map, _vgroups)
return True
def _get_subgroups(self, _vgroup, _elements,
_vgroups, _vms, _affinity_map):
for vk in _elements[_vgroup.uuid]["properties"]["resources"]:
if vk in _vms.keys():
_vgroup.subvgroups[vk] = _vms[vk]
_vms[vk].survgroup = _vgroup
_affinity_map[vk] = _vgroup
self._add_implicit_diversity_groups(
_vgroup, _vms[vk].diversity_groups)
self._add_implicit_exclusivity_groups(
_vgroup, _vms[vk].exclusivity_groups)
self._add_memberships(_vgroup, _vms[vk])
del _vms[vk]
elif vk in _vgroups.keys():
vg = _vgroups[vk]
if LEVELS.index(vg.level) > LEVELS.index(_vgroup.level):
self.status = ("grouping scope: nested group's level is "
"higher")
return False
if vg.vgroup_type == "DIV" or vg.vgroup_type == "EX":
if not self._merge_subgroups(_vgroup, vg.subvgroups,
_vms, _vgroups,
_elements, _affinity_map):
return False
del _vgroups[vk]
else:
if self._exist_in_subgroups(vk, _vgroup) is None:
if not self._get_subgroups(vg, _elements, _vgroups,
_vms, _affinity_map):
return False
_vgroup.subvgroups[vk] = vg
vg.survgroup = _vgroup
_affinity_map[vk] = _vgroup
self._add_implicit_diversity_groups(
_vgroup, vg.diversity_groups)
self._add_implicit_exclusivity_groups(
_vgroup, vg.exclusivity_groups)
self._add_memberships(_vgroup, vg)
del _vgroups[vk]
else:
if vk not in _affinity_map.keys():
self.status = "invalid resource = " + vk
return False
if _affinity_map[vk].uuid != _vgroup.uuid:
if self._exist_in_subgroups(vk, _vgroup) is None:
self._set_implicit_grouping(
vk, _vgroup, _affinity_map, _vgroups)
return True
def _add_implicit_diversity_groups(self, _vgroup, _diversity_groups):
for dz, level in _diversity_groups.iteritems():
l = level.split(":", 1)[0]
if LEVELS.index(l) >= LEVELS.index(_vgroup.level):
_vgroup.diversity_groups[dz] = level
def _add_implicit_exclusivity_groups(self, _vgroup, _exclusivity_groups):
for ex, level in _exclusivity_groups.iteritems():
l = level.split(":", 1)[0]
if LEVELS.index(l) >= LEVELS.index(_vgroup.level):
_vgroup.exclusivity_groups[ex] = level
def _add_memberships(self, _vgroup, _v):
if isinstance(_v, VM) or isinstance(_v, VGroup):
for extra_specs in _v.extra_specs_list:
_vgroup.extra_specs_list.append(extra_specs)
if isinstance(_v, VM) and _v.availability_zone is not None:
if _v.availability_zone not in _vgroup.availability_zone_list:
_vgroup.availability_zone_list.append(_v.availability_zone)
if isinstance(_v, VGroup):
for az in _v.availability_zone_list:
if az not in _vgroup.availability_zone_list:
_vgroup.availability_zone_list.append(az)
''' take vk's most top parent as a s_vg's child vgroup '''
def _set_implicit_grouping(self, _vk, _s_vg, _affinity_map, _vgroups):
t_vg = _affinity_map[_vk] # where _vk currently belongs to
if t_vg.uuid in _affinity_map.keys():
# if the parent belongs to the other parent vgroup
self._set_implicit_grouping(
t_vg.uuid, _s_vg, _affinity_map, _vgroups)
else:
if LEVELS.index(t_vg.level) > LEVELS.index(_s_vg.level):
t_vg.level = _s_vg.level
if self._exist_in_subgroups(t_vg.uuid, _s_vg) is None:
_s_vg.subvgroups[t_vg.uuid] = t_vg
t_vg.survgroup = _s_vg
_affinity_map[t_vg.uuid] = _s_vg
self._add_implicit_diversity_groups(
_s_vg, t_vg.diversity_groups)
self._add_implicit_exclusivity_groups(
_s_vg, t_vg.exclusivity_groups)
self._add_memberships(_s_vg, t_vg)
del _vgroups[t_vg.uuid]
def _exist_in_subgroups(self, _vk, _vg):
containing_vg_uuid = None
for vk, v in _vg.subvgroups.iteritems():
if vk == _vk:
containing_vg_uuid = _vg.uuid
break
else:
if isinstance(v, VGroup):
containing_vg_uuid = self._exist_in_subgroups(_vk, v)
if containing_vg_uuid is not None:
break
return containing_vg_uuid