d5a790caab
nailgun.errors have a huge set of exceptions but without hierarchy. This patch remove exception generation from dict and make it explicitly with python classes and add some exceptions hierarchy. Now all network errors inherit from NetworkException and same for other exceptions. Change-Id: I9a2c6b358ea02a16711da74562308664ad7aed97 Closes-bug: #1566195
114 lines
3.5 KiB
Python
114 lines
3.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2015 Mirantis, Inc.
|
|
#
|
|
# 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 abc
|
|
import six
|
|
|
|
from nailgun import errors
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class MergePolicy(object):
|
|
"""Policy to merge attributes of plugins."""
|
|
|
|
@abc.abstractmethod
|
|
def apply_patch(self, target, patch):
|
|
"""Applies patch to the target.
|
|
|
|
:param target: the origin object, the target can be modified.
|
|
:param patch: the modifications for merging with original
|
|
:return: the patched object.
|
|
"""
|
|
|
|
|
|
class NetworkRoleMergePolicy(MergePolicy):
|
|
"""Policy for merging network roles."""
|
|
|
|
def __init__(self):
|
|
self.rules = {'vip': NetworkRoleMergePolicy._patch_vips}
|
|
|
|
@staticmethod
|
|
def _patch_vips(target, patch):
|
|
"""Patches VIP attribute for network role.
|
|
|
|
:param: target: the origin VIPs.
|
|
:type target: list
|
|
:param patch: the VIPs, that will be added to origin
|
|
:type patch: list
|
|
:return: the patched VIPs
|
|
"""
|
|
seen = dict((vip['name'], vip) for vip in target)
|
|
|
|
# This is a workaround for 8.0 release to allow
|
|
# moving controllers to different racks. Will be
|
|
# replaced by a different solution in 9.0
|
|
if len(patch) == 0:
|
|
return []
|
|
# END OF WORKAROUND
|
|
|
|
for vip in patch:
|
|
if vip['name'] in seen:
|
|
if vip != seen[vip['name']]:
|
|
raise ValueError(
|
|
"VIP '{0}' conflicts with existing one"
|
|
.format(vip['name'])
|
|
)
|
|
else:
|
|
seen[vip['name']] = vip
|
|
target.append(vip)
|
|
|
|
return target
|
|
|
|
def apply_patch(self, target, patch):
|
|
"""Tries to apply patch to the target.
|
|
|
|
Conflicts will be resolved according to the
|
|
predefined rules.
|
|
|
|
:param target: the origin network role
|
|
:type target: dict
|
|
:param patch: the modifications for merging with origin
|
|
:type patch: dict
|
|
:raises: errors.UnresolvableConflict
|
|
"""
|
|
|
|
target_props = target['properties']
|
|
patch_props = patch['properties']
|
|
|
|
conflict = set(target_props) & set(patch_props)
|
|
mergeable = set(self.rules)
|
|
|
|
# Exclude fields that has same value
|
|
for name in (conflict - mergeable):
|
|
if target_props[name] != patch_props[name]:
|
|
raise errors.UnresolvableConflict(
|
|
"Cannot apply patch for attribute {0}: conflict"
|
|
.format(name)
|
|
)
|
|
conflict.remove(name)
|
|
|
|
for name in conflict:
|
|
try:
|
|
target_props[name] = self.rules[name](
|
|
target_props[name],
|
|
patch_props[name]
|
|
)
|
|
except Exception as e:
|
|
raise errors.UnresolvableConflict(
|
|
"Cannot apply patch for attribute {0}: {1}"
|
|
.format(name, e)
|
|
)
|