summaryrefslogtreecommitdiffstats
path: root/deploy/install_fuel_master.py
blob: 4f6a0527b879e9947c3f6ec69839bd88dc5b7fce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd
###############################################################################
# Copyright (c) 2015 Ericsson AB and others.
# szilard.cserey@ericsson.com
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
###############################################################################

import time
import os
import glob
from ssh_client import SSHClient
from dha_adapters.libvirt_adapter import LibvirtAdapter

from common import (
    log,
    err,
    clean,
    delete,
)

TRANSPLANT_FUEL_SETTINGS = 'transplant_fuel_settings.py'
BOOTSTRAP_ADMIN = '/usr/local/sbin/bootstrap_admin_node'
FUEL_CLIENT_CONFIG = '/etc/fuel/client/config.yaml'
PLUGINS_DIR = '~/plugins'
LOCAL_PLUGIN_FOLDER = '/opt/opnfv'
IGNORABLE_FUEL_ERRORS = ['does not update installed package',
                         'Couldn\'t resolve host']


class InstallFuelMaster(object):

    def __init__(self, dea_file, dha_file, fuel_ip, fuel_username,
                 fuel_password, fuel_node_id, iso_file, work_dir,
                 fuel_plugins_dir, no_plugins):
        self.dea_file = dea_file
        self.dha = LibvirtAdapter(dha_file)
        self.fuel_ip = fuel_ip
        self.fuel_username = fuel_username
        self.fuel_password = fuel_password
        self.fuel_node_id = fuel_node_id
        self.iso_file = iso_file
        self.iso_dir = os.path.dirname(self.iso_file)
        self.work_dir = work_dir
        self.fuel_plugins_dir = fuel_plugins_dir
        self.no_plugins = no_plugins
        self.file_dir = os.path.dirname(os.path.realpath(__file__))
        self.ssh = SSHClient(self.fuel_ip, self.fuel_username,
                             self.fuel_password)

    def install(self):
        log('Start Fuel Installation')

        self.dha.node_power_off(self.fuel_node_id)

        log('Zero the MBR')
        self.dha.node_zero_mbr(self.fuel_node_id)

        self.dha.node_set_boot_order(self.fuel_node_id, ['disk', 'iso'])

        try:
            self.proceed_with_installation()
        except Exception as e:
            self.post_install_cleanup()
            err(e)

    def proceed_with_installation(self):
        log('Eject ISO')
        self.dha.node_eject_iso(self.fuel_node_id)

        log('Insert ISO %s' % self.iso_file)
        self.dha.node_insert_iso(self.fuel_node_id, self.iso_file)

        self.dha.node_power_on(self.fuel_node_id)

        log('Waiting for Fuel master to accept SSH')
        self.wait_for_node_up()

        log('Wait until Fuel menu is up')
        fuel_menu_pid = self.wait_until_fuel_menu_up()

        log('Inject our own astute.yaml settings')
        self.inject_own_astute_yaml()

        log('Let the Fuel deployment continue')
        log('Found FUEL menu as PID %s, now killing it' % fuel_menu_pid)
        self.ssh_exec_cmd('kill %s' % fuel_menu_pid, False)

        log('Wait until installation is complete')
        self.wait_until_installation_completed()

        log('Waiting for one minute for Fuel to stabilize')
        time.sleep(60)

        self.delete_deprecated_fuel_client_config()

        if not self.no_plugins:

            self.collect_plugin_files()

            self.install_plugins()

        self.post_install_cleanup()

        log('Fuel Master installed successfully !')

    def collect_plugin_files(self):
        with self.ssh as s:
            s.exec_cmd('mkdir %s' % PLUGINS_DIR)
            if self.fuel_plugins_dir:
                for f in glob.glob('%s/*.rpm' % self.fuel_plugins_dir):
                    s.scp_put(f, PLUGINS_DIR)

    def install_plugins(self):
        log('Installing Fuel Plugins')
        plugin_files = []
        with self.ssh as s:
            for plugin_location in [PLUGINS_DIR, LOCAL_PLUGIN_FOLDER]:
                s.exec_cmd('mkdir -p %s' % plugin_location)
                r = s.exec_cmd('find %s -type f -name \'*.rpm\''
                               % plugin_location)
                plugin_files.extend(r.splitlines())
            for f in plugin_files:
                log('Found plugin %s, installing ...' % f)
                r, e = s.exec_cmd('fuel plugins --install %s' % f, False)
                printout = r + e if e else r
                if e and all([err not in printout
                              for err in IGNORABLE_FUEL_ERRORS]):
                    raise Exception('Installation of Fuel Plugin %s '
                                    'failed: %s' % (f, e))

    def wait_for_node_up(self):
        WAIT_LOOP = 240
        SLEEP_TIME = 10
        success = False
        for i in range(WAIT_LOOP):
            try:
                self.ssh.open()
                success = True
                break
            except Exception:
                log('Trying to SSH into Fuel VM %s ... sleeping %s seconds'
                    % (self.fuel_ip, SLEEP_TIME))
                time.sleep(SLEEP_TIME)
            finally:
                self.ssh.close()

        if not success:
            raise Exception('Could not SSH into Fuel VM %s' % self.fuel_ip)

    def wait_until_fuel_menu_up(self):
        WAIT_LOOP = 60
        SLEEP_TIME = 10
        CMD = 'ps -ef'
        SEARCH = 'fuelmenu'
        fuel_menu_pid = None
        with self.ssh:
            for i in range(WAIT_LOOP):
                ret = self.ssh.exec_cmd(CMD)
                fuel_menu_pid = self.get_fuel_menu_pid(ret, SEARCH)
                if not fuel_menu_pid:
                    time.sleep(SLEEP_TIME)
                else:
                    break
        if not fuel_menu_pid:
            raise Exception('Could not find the Fuel Menu Process ID')
        return fuel_menu_pid

    def get_fuel_menu_pid(self, printout, search):
        for line in printout.splitlines():
            if line.endswith(search):
                return clean(line)[1]

    def ssh_exec_cmd(self, cmd, check=True):
        with self.ssh:
            ret = self.ssh.exec_cmd(cmd, check=check)
        return ret

    def inject_own_astute_yaml(self):
        with self.ssh as s:
            s.exec_cmd('rm -rf %s' % self.work_dir, False)
            s.exec_cmd('mkdir %s' % self.work_dir)
            s.scp_put(self.dea_file, self.work_dir)
            s.scp_put('%s/common.py' % self.file_dir, self.work_dir)
            s.scp_put('%s/dea.py' % self.file_dir, self.work_dir)
            s.scp_put('%s/transplant_fuel_settings.py'
                      % self.file_dir, self.work_dir)
            log('Modifying Fuel astute')
            s.run('python %s/%s %s/%s'
                  % (self.work_dir, TRANSPLANT_FUEL_SETTINGS,
                     self.work_dir, os.path.basename(self.dea_file)))

    def wait_until_installation_completed(self):
        WAIT_LOOP = 360
        SLEEP_TIME = 10
        CMD = 'ps -ef | grep %s | grep -v grep' % BOOTSTRAP_ADMIN

        install_completed = False
        with self.ssh:
            for i in range(WAIT_LOOP):
                ret = self.ssh.exec_cmd(CMD)
                if not ret:
                    install_completed = True
                    break
                else:
                    time.sleep(SLEEP_TIME)

        if not install_completed:
            raise Exception('Fuel installation did not complete')

    def post_install_cleanup(self):
        log('Eject ISO file %s' % self.iso_file)
        self.dha.node_eject_iso(self.fuel_node_id)
        delete(self.iso_dir)

    def delete_deprecated_fuel_client_config(self):
        with self.ssh as s:
            response, error = s.exec_cmd('fuel -v', False)
        if (error and
            'DEPRECATION WARNING' in error and FUEL_CLIENT_CONFIG in error):
            log('Delete deprecated fuel client config %s' % FUEL_CLIENT_CONFIG)
            with self.ssh as s:
                s.exec_cmd('rm %s' % FUEL_CLIENT_CONFIG, False)