149 lines
5.5 KiB
Python
149 lines
5.5 KiB
Python
# 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 itertools
|
|
import logging
|
|
import os.path
|
|
|
|
|
|
from cliff import command as cmd
|
|
from fuelclient.objects import environment as environment_obj
|
|
from fuelclient.objects import node as node_obj
|
|
|
|
from octane.handlers import upgrade as upgrade_handlers
|
|
from octane import magic_consts
|
|
from octane.util import docker
|
|
from octane.util import env as env_util
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def check_isolation(env, nodes, isolated):
|
|
seed_env_controllers = list(env_util.get_nodes(env, ['controller']))
|
|
is_one_controller_to_upgrade = len(nodes) == 1 and \
|
|
'controller' in nodes[0].data['roles']
|
|
|
|
if not (seed_env_controllers or isolated and is_one_controller_to_upgrade):
|
|
raise Exception("Only one controller is supported to be upgraded as "
|
|
"the first controller in the new environment "
|
|
"in the isolation mode.")
|
|
|
|
if isolated and seed_env_controllers:
|
|
raise Exception("Only the first controller is supported to be "
|
|
"upgraded in the isolation mode.")
|
|
|
|
|
|
def check_sanity(env_id, nodes):
|
|
one_orig_id = None
|
|
|
|
for node in nodes:
|
|
node_id = node.data['id']
|
|
orig_id = node.data['cluster']
|
|
if orig_id == env_id:
|
|
raise Exception(
|
|
"Cannot upgrade node with ID %s: it's already in cluster with "
|
|
"ID %s", node_id, env_id,
|
|
)
|
|
if orig_id:
|
|
if one_orig_id and orig_id != one_orig_id:
|
|
raise Exception(
|
|
"Not upgrading nodes from different clusters: %s and %s",
|
|
orig_id, one_orig_id,
|
|
)
|
|
one_orig_id = orig_id
|
|
|
|
|
|
def upgrade_node(env_id, node_ids, isolated=False, provision=True, roles=None,
|
|
live_migration=True):
|
|
# From check_deployment_status
|
|
env = environment_obj.Environment(env_id)
|
|
nodes = [node_obj.Node(node_id) for node_id in node_ids]
|
|
|
|
check_sanity(env_id, nodes)
|
|
check_isolation(env, nodes, isolated)
|
|
|
|
call_handlers = upgrade_handlers.get_nodes_handlers(
|
|
nodes, env, isolated, live_migration)
|
|
|
|
call_handlers('preupgrade')
|
|
call_handlers('prepare')
|
|
env_util.move_nodes(env, nodes, provision, roles)
|
|
|
|
# NOTE(aroma): copying of VIPs must be done after node reassignment
|
|
# as according to [1] otherwise the operation will not take any effect
|
|
# [1]: https://bugs.launchpad.net/fuel/+bug/1549254
|
|
env_util.copy_vips(env)
|
|
call_handlers('predeploy')
|
|
if isolated:
|
|
tasks_to_skip = set(
|
|
itertools.chain.from_iterable(call_handlers('skip_tasks'))
|
|
)
|
|
LOG.info("Tasks to skip: {0}".format(', '.join(tasks_to_skip)))
|
|
env_util.deploy_nodes_without_tasks(env, nodes, tasks_to_skip)
|
|
else:
|
|
env_util.deploy_changes(env, nodes)
|
|
call_handlers('postdeploy')
|
|
# TODO(akscram): The upgrade_levels parameters have to be handled by the
|
|
# advanced configuration feature for OpenStack services (openstack-config).
|
|
env_util.set_upgrade_levels(env)
|
|
|
|
|
|
def copy_patches_folder_to_nailgun():
|
|
dest_folder = '/tmp'
|
|
folder = os.path.join(magic_consts.CWD, 'patches')
|
|
docker.put_files_to_docker('nailgun', dest_folder, folder)
|
|
|
|
|
|
def list_roles(s):
|
|
return s.split(',')
|
|
|
|
|
|
class UpgradeNodeCommand(cmd.Command):
|
|
"""Move nodes to environment and upgrade the node"""
|
|
|
|
def get_parser(self, prog_name):
|
|
parser = super(UpgradeNodeCommand, self).get_parser(prog_name)
|
|
parser.add_argument(
|
|
'--no-provision', dest='provision', action='store_false',
|
|
default=True,
|
|
help="Perform reprovisioning of nodes during the upgrade. "
|
|
"(default: True).")
|
|
parser.add_argument(
|
|
'--roles', type=list_roles, nargs='?',
|
|
help="Assign given roles to the specified nodes or do not specify "
|
|
"them at all to preserve the current roles.")
|
|
parser.add_argument(
|
|
'--isolated', action='store_true',
|
|
help="Isolate node's network from original cluster")
|
|
parser.add_argument(
|
|
'env_id', type=int, metavar='ENV_ID',
|
|
help="ID of target environment")
|
|
parser.add_argument(
|
|
'node_ids', type=int, metavar='NODE_ID', nargs='+',
|
|
help="IDs of nodes to be moved")
|
|
parser.add_argument(
|
|
'--no-live-migration',
|
|
action='store_false',
|
|
dest="live_migration",
|
|
default=True,
|
|
help="Run migration on ceph-osd or compute nodes in one command. "
|
|
"It can prevent to cluster downtime on deploy period. "
|
|
"(default: True).")
|
|
return parser
|
|
|
|
def take_action(self, parsed_args):
|
|
upgrade_node(parsed_args.env_id, parsed_args.node_ids,
|
|
isolated=parsed_args.isolated,
|
|
provision=parsed_args.provision,
|
|
roles=parsed_args.roles,
|
|
live_migration=parsed_args.live_migration)
|