231 lines
7.9 KiB
Python
231 lines
7.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2014 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 glob
|
|
import io
|
|
import logging
|
|
import os
|
|
|
|
import requests
|
|
import six
|
|
|
|
from fuel_upgrade.clients import NailgunClient
|
|
from fuel_upgrade.engines.base import UpgradeEngine
|
|
from fuel_upgrade import utils
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class OpenStackUpgrader(UpgradeEngine):
|
|
"""OpenStack Upgrader.
|
|
|
|
The class is designed to do the following tasks:
|
|
|
|
* install manifests in the system
|
|
* add new releases to nailgun's database
|
|
* add notification about new releases
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(OpenStackUpgrader, self).__init__(*args, **kwargs)
|
|
|
|
#: a list of releases to install
|
|
self.releases = self._read_releases()
|
|
#: a nailgun object - api wrapper
|
|
self.nailgun = NailgunClient(**self.config.endpoints['nginx_nailgun'])
|
|
|
|
self._reset_state()
|
|
|
|
def upgrade(self):
|
|
self._reset_state()
|
|
|
|
self.install_puppets()
|
|
self.install_releases()
|
|
self.install_versions()
|
|
|
|
def rollback(self):
|
|
self.remove_releases()
|
|
self.remove_puppets()
|
|
self.remove_versions()
|
|
|
|
def install_puppets(self):
|
|
logger.info('Installing puppet manifests...')
|
|
|
|
sources = glob.glob(self.config.openstack['puppets']['src'])
|
|
for source in sources:
|
|
destination = os.path.join(
|
|
self.config.openstack['puppets']['dst'],
|
|
os.path.basename(source))
|
|
utils.copy(source, destination)
|
|
|
|
def remove_puppets(self):
|
|
logger.info('Removing puppet manifests...')
|
|
|
|
sources = glob.glob(self.config.openstack['puppets']['src'])
|
|
for source in sources:
|
|
destination = os.path.join(
|
|
self.config.openstack['puppets']['dst'],
|
|
os.path.basename(source))
|
|
utils.remove(destination)
|
|
|
|
def install_versions(self):
|
|
"""Copy openstack release versions"""
|
|
logger.info('Copy openstack release versions...')
|
|
release_versions_cfg = self.config.openstack['release_versions']
|
|
versions = glob.glob(release_versions_cfg['src'])
|
|
|
|
utils.create_dir_if_not_exists(release_versions_cfg['dst'])
|
|
for version_file in versions:
|
|
dst = os.path.join(
|
|
release_versions_cfg['dst'],
|
|
os.path.basename(version_file))
|
|
utils.copy(version_file, dst)
|
|
|
|
def remove_versions(self):
|
|
"""Copy openstack release versions"""
|
|
logger.info('Copy openstack release versions...')
|
|
release_versions_cfg = self.config.openstack['release_versions']
|
|
versions = glob.glob(release_versions_cfg['src'])
|
|
|
|
for version_file in versions:
|
|
dst = os.path.join(
|
|
release_versions_cfg['dst'],
|
|
os.path.basename(version_file))
|
|
utils.remove(dst)
|
|
|
|
def install_releases(self):
|
|
# add only new releases to nailgun and inject paths to
|
|
# base repo if needed
|
|
existing_releases = self.nailgun.get_releases()
|
|
releases = self._get_unique_releases(self.releases, existing_releases)
|
|
|
|
# upload unexisting releases
|
|
for release in releases:
|
|
# register new release
|
|
logger.debug('Register a new release: %s (%s)',
|
|
release['name'],
|
|
release['version'])
|
|
response = self.nailgun.create_release(release)
|
|
# save release id for futher possible rollback
|
|
self._rollback_ids['release'].append(response['id'])
|
|
self.upload_release_deployment_tasks(response)
|
|
|
|
if not release.get('state', 'available') == 'available':
|
|
continue
|
|
|
|
# add notification abot successfull releases
|
|
logger.debug('Add notification about new release: %s (%s)',
|
|
release['name'],
|
|
release['version'])
|
|
response = self.nailgun.create_notification({
|
|
'topic': 'release',
|
|
'message': 'New release available: {0} ({1})'.format(
|
|
release['name'],
|
|
release['version'],
|
|
),
|
|
})
|
|
# save notification id for futher possible rollback
|
|
self._rollback_ids['notification'].append(response['id'])
|
|
|
|
def upload_release_deployment_tasks(self, release):
|
|
"""Upload deployment tasks for release
|
|
|
|
Performs os.walk by puppet src, matches all files with tasks
|
|
of given pattern and uploads this for release.
|
|
|
|
:param release: dict representation of release
|
|
"""
|
|
tasks = []
|
|
release_puppet_path = os.path.join(
|
|
self.config.openstack['puppets']['dst'], release['version'])
|
|
|
|
for file_path in utils.iterfiles_filter(
|
|
release_puppet_path,
|
|
self.config.deployment_tasks_file_pattern):
|
|
|
|
tasks.extend(utils.read_from_yaml(file_path))
|
|
|
|
self.nailgun.put_deployment_tasks(release, tasks)
|
|
|
|
def remove_releases(self):
|
|
"""Remove all releases that are created by current session."""
|
|
for release_id in reversed(self._rollback_ids['release']):
|
|
try:
|
|
logger.debug('Removing release with ID=%s', release_id)
|
|
self.nailgun.remove_release(release_id)
|
|
except (
|
|
requests.exceptions.HTTPError
|
|
) as exc:
|
|
logger.exception('%s', six.text_type(exc))
|
|
|
|
for notif_id in reversed(self._rollback_ids['notification']):
|
|
try:
|
|
logger.debug('Removing notification with ID=%s', notif_id)
|
|
self.nailgun.remove_notification(notif_id)
|
|
except (
|
|
requests.exceptions.HTTPError
|
|
) as exc:
|
|
logger.exception('%s', six.text_type(exc))
|
|
|
|
def _reset_state(self):
|
|
"""Remove rollback IDs from the arrays."""
|
|
#: a list of ids that have to be removed in case of rollback
|
|
self._rollback_ids = {
|
|
'release': [],
|
|
'notification': [],
|
|
}
|
|
|
|
@classmethod
|
|
def _get_unique_releases(cls, releases, existing_releases):
|
|
"""Returns a list of releases that aren't exist yet.
|
|
|
|
:param releases: a list of releases to filter
|
|
:param existing_releases: a list of existing releases
|
|
:returns: a list of unique releases
|
|
"""
|
|
existing_releases = [
|
|
(r['name'], r['version']) for r in existing_releases
|
|
]
|
|
|
|
unique = lambda r: (r['name'], r['version']) not in existing_releases
|
|
return [r for r in releases if unique(r)]
|
|
|
|
def _read_releases(self):
|
|
"""Returns a list of releases in a dict representation."""
|
|
releases = []
|
|
|
|
# read releases from a set of files
|
|
for release_yaml in glob.glob(self.config.openstack['releases']):
|
|
with io.open(release_yaml, 'r', encoding='utf-8') as f:
|
|
releases.extend(utils.load_fixture(f))
|
|
|
|
return releases
|
|
|
|
@property
|
|
def required_free_space(self):
|
|
spaces = {
|
|
self.config.openstack['puppets']['dst']:
|
|
glob.glob(self.config.openstack['puppets']['src']), }
|
|
|
|
for dst, srcs in six.iteritems(spaces):
|
|
size = 0
|
|
for src in srcs:
|
|
size += utils.dir_size(src)
|
|
spaces[dst] = size
|
|
|
|
return spaces
|