# 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 collections import logging from cliff import command as cmd from fuelclient import objects from octane import magic_consts from octane.util import apt from octane.util import deployment as deploy from octane.util import helpers from octane.util import ssh LOG = logging.getLogger(__name__) def check_sanity(nodes, release): if release.data['state'] not in ('available', 'manageonly'): raise Exception( "Release with id {0} is not available (at least manageonly)." .format(release.id) ) env_id = nodes[0].env.id for node in nodes: if node.env.id != env_id: raise Exception( "Nodes have different clusters." ) env_id = node.env.id if 'compute' not in node.data['roles']: raise Exception( "Preupgrade procedure is available only for compute nodes. " "Node with id {0} is not a compute.".format(node.id) ) def change_repositories(node, repos): ssh.remove_all_files_from_dirs(['/etc/apt/sources.list.d', '/etc/apt/preferences.d'], node) sftp = ssh.sftp(node) for repo in repos: filename_source, content_source = apt.create_repo_source(repo) ssh.write_content_to_file(sftp, filename_source, content_source) if repo['priority']: filename_pref, content_pref = apt.create_repo_preferences(repo) ssh.write_content_to_file(sftp, filename_pref, content_pref) ssh.call(['apt-get', 'update'], node=node) def stop_compute_services(node): ssh.call(['stop', 'nova-compute'], node=node) ssh.call(['stop', 'neutron-plugin-openvswitch-agent'], node=node) def get_repos(release, master_ip=''): repos = (release.data['attributes_metadata']['editable']['repo_setup'] ['repos']['value']) version = release.data['version'] environment_version = version.split('-')[1] settings_cls = collections.namedtuple("settings", ["MASTER_IP", "release"]) release_cls = collections.namedtuple("release", ["version", "environment_version"]) settings = settings_cls(master_ip, release_cls(version, environment_version)) for repo in repos: repo['uri'] = repo['uri'].format(settings=settings, cluster=settings) return repos def get_package_list(release): version = release.data['version'] try: packages = magic_consts.COMPUTE_PREUPGRADE_PACKAGES[version] except KeyError: LOG.exception("Info about packages for a release with id {0} " "does not exist".format(release.id)) raise return packages def add_upgrade_attrs_to_settings(env, repos, packages): attrs = env.get_settings_data() attrs['editable']['repo_setup']['preupgrade_compute'] = {'value': repos, 'type': 'hidden'} attrs['editable']['common']['preupgrade_packages'] = {'value': packages, 'type': 'hidden'} env.set_settings_data(attrs) def preupgrade_compute(release_id, node_ids): nodes = [objects.node.Node(node_id) for node_id in node_ids] release = objects.Release(release_id) check_sanity(nodes, release) master_ip = helpers.get_astute_dict()["ADMIN_NETWORK"]['ipaddress'] repos = get_repos(release, master_ip) packages = get_package_list(release) for node in nodes: change_repositories(node, repos) stop_compute_services(node) apt.upgrade_packages(node, packages) def preupgrade_compute_with_graph(release_id, node_ids): nodes = [objects.node.Node(node_id) for node_id in node_ids] release = objects.Release(release_id) check_sanity(nodes, release) master_ip = helpers.get_astute_dict()["ADMIN_NETWORK"]['ipaddress'] repos = get_repos(release, master_ip) packages = get_package_list(release) env_id = nodes[0].env.id env = objects.environment.Environment(env_id) # Add following data to cluster attributes: # - new repositories # - list of packages to be updated add_upgrade_attrs_to_settings(env, repos, packages) deploy.upload_graph(env_id, "orig") deploy.execute_graph_and_wait('preupgrade-compute', env_id, node_ids) class PreupgradeComputeCommand(cmd.Command): """Preupgrade compute""" def get_parser(self, prog_name): parser = super(PreupgradeComputeCommand, self).get_parser(prog_name) parser.add_argument( 'release_id', type=int, metavar='RELEASE_ID', help="Release that repositories will be taken from" ) parser.add_argument( 'node_ids', type=int, metavar='NODE_ID', help="IDs of compute nodes to be preupgraded", nargs="+") parser.add_argument( '--with-graph', action='store_true', help='EXPERIMENTAL: Use Fuel deployment graphs' ' instead of python-based commands.') return parser def take_action(self, parsed_args): if parsed_args.with_graph: preupgrade_compute_with_graph(parsed_args.release_id, parsed_args.node_ids) else: preupgrade_compute(parsed_args.release_id, parsed_args.node_ids)