fuel-nailgun-extension-clus.../cluster_upgrade/upgrade.py

218 lines
8.2 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 copy
import six
from nailgun import consts
from nailgun.extensions.network_manager.objects.serializers import \
network_configuration
from nailgun import objects
from nailgun import utils
from . import transformations # That's weird, but that's how hacking likes
from .objects import adapters
from .transformations import cluster as cluster_trs
from .transformations import vip
from .transformations import volumes as volumes_trs
def merge_attributes(a, b):
"""Merge values of editable attributes.
The values of the b attributes have precedence over the values
of the a attributes.
"""
attrs = copy.deepcopy(b)
for section, pairs in six.iteritems(attrs):
if section == "repo_setup" or section not in a:
continue
a_values = a[section]
for key, values in six.iteritems(pairs):
if key != "metadata" and key in a_values:
values["value"] = a_values[key]["value"]
return attrs
def merge_nets(a, b):
new_settings = copy.deepcopy(b)
source_networks = dict((n["name"], n) for n in a["networks"])
for net in new_settings["networks"]:
if net["name"] not in source_networks:
continue
source_net = source_networks[net["name"]]
for key, value in six.iteritems(net):
if (key not in ("cluster_id", "id", "meta", "group_id") and
key in source_net):
net[key] = source_net[key]
networking_params = new_settings["networking_parameters"]
source_params = a["networking_parameters"]
for key, value in six.iteritems(networking_params):
if key not in source_params:
continue
networking_params[key] = source_params[key]
return new_settings
class UpgradeHelper(object):
network_serializers = {
consts.CLUSTER_NET_PROVIDERS.neutron:
network_configuration.NeutronNetworkConfigurationSerializer,
consts.CLUSTER_NET_PROVIDERS.nova_network:
network_configuration.NovaNetworkConfigurationSerializer,
}
cluster_transformations = transformations.Lazy(cluster_trs.Manager)
vip_transformations = transformations.Lazy(vip.Manager)
volumes_transformations = transformations.Lazy(volumes_trs.Manager)
@classmethod
def clone_cluster(cls, orig_cluster, data):
from .objects import relations
new_cluster = cls.create_cluster_clone(orig_cluster, data)
cls.copy_attributes(orig_cluster, new_cluster)
cls.copy_network_config(orig_cluster, new_cluster)
relations.UpgradeRelationObject.create_relation(orig_cluster.id,
new_cluster.id)
cls.change_env_settings(orig_cluster, new_cluster)
return new_cluster
@classmethod
def create_cluster_clone(cls, orig_cluster, data):
create_data = orig_cluster.get_create_data()
create_data["name"] = data["name"]
create_data["release_id"] = data["release_id"]
new_cluster = adapters.NailgunClusterAdapter.create(create_data)
return new_cluster
@classmethod
def copy_attributes(cls, orig_cluster, new_cluster):
attrs = cls.cluster_transformations.apply(
orig_cluster.release.environment_version,
new_cluster.release.environment_version,
{
'editable': orig_cluster.editable_attrs,
'generated': orig_cluster.generated_attrs,
},
)
new_cluster.generated_attrs = utils.dict_merge(
new_cluster.generated_attrs,
attrs['generated'],
)
new_cluster.editable_attrs = merge_attributes(
attrs['editable'],
new_cluster.editable_attrs,
)
@classmethod
def change_env_settings(cls, orig_cluster, new_cluster):
attrs = new_cluster.attributes
attrs['editable']['provision']['method']['value'] = 'image'
@classmethod
def copy_network_config(cls, orig_cluster, new_cluster):
nets_serializer = cls.network_serializers[orig_cluster.net_provider]
nets = merge_nets(
nets_serializer.serialize_for_cluster(orig_cluster.cluster),
nets_serializer.serialize_for_cluster(new_cluster.cluster))
new_net_manager = new_cluster.get_network_manager()
new_net_manager.update(nets)
@classmethod
def copy_vips(cls, orig_cluster, new_cluster):
orig_net_manager = orig_cluster.get_network_manager()
new_net_manager = new_cluster.get_network_manager()
vips = {}
assigned_vips = orig_net_manager.get_assigned_vips()
for ng_name in (consts.NETWORKS.public, consts.NETWORKS.management):
vips[ng_name] = assigned_vips[ng_name]
vips = cls.vip_transformations.apply(
orig_cluster.release.environment_version,
new_cluster.release.environment_version,
vips
)
new_net_manager.assign_given_vips_for_net_groups(vips)
new_net_manager.assign_vips_for_net_groups()
@classmethod
def get_node_roles(cls, reprovision, current_roles, given_roles):
"""Return roles depending on the reprovisioning status.
In case the node should be re-provisioned, only pending roles
should be set, otherwise for an already provisioned and deployed
node only actual roles should be set. In the both case the
given roles will have precedence over the existing.
:param reprovision: boolean, if set to True then the node should
be re-provisioned
:param current_roles: a list of current roles of the node
:param given_roles: a list of roles that should be assigned to
the node
:returns: a tuple of a list of roles and a list of pending roles
that will be assigned to the node
"""
roles_to_assign = given_roles if given_roles else current_roles
if reprovision:
roles, pending_roles = [], roles_to_assign
else:
roles, pending_roles = roles_to_assign, []
return roles, pending_roles
@classmethod
def assign_node_to_cluster(cls, node, seed_cluster, roles, pending_roles):
orig_cluster = adapters.NailgunClusterAdapter.get_by_uid(
node.cluster_id)
volumes = cls.volumes_transformations.apply(
orig_cluster.release.environment_version,
seed_cluster.release.environment_version,
node.get_volumes(),
)
node.set_volumes(volumes)
orig_manager = orig_cluster.get_network_manager()
netgroups_id_mapping = cls.get_netgroups_id_mapping(
orig_cluster, seed_cluster)
node.update_cluster_assignment(seed_cluster, roles, pending_roles)
objects.Node.set_netgroups_ids(node, netgroups_id_mapping)
if not seed_cluster.network_template:
orig_manager.set_nic_assignment_netgroups_ids(
node, netgroups_id_mapping)
orig_manager.set_bond_assignment_netgroups_ids(
node, netgroups_id_mapping)
node.add_pending_change(consts.CLUSTER_CHANGES.interfaces)
@classmethod
def get_netgroups_id_mapping(self, orig_cluster, seed_cluster):
orig_ng = orig_cluster.get_network_groups()
seed_ng = seed_cluster.get_network_groups()
seed_ng_dict = dict((ng.name, ng.id) for ng in seed_ng)
mapping = dict((ng.id, seed_ng_dict[ng.name]) for ng in orig_ng)
mapping[orig_cluster.get_admin_network_group().id] = \
seed_cluster.get_admin_network_group().id
return mapping