boot.py 6.8 KB
Newer Older
1
# -*- coding: utf-8 -*-
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
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################
29

Bryton Lacquement's avatar
Bryton Lacquement committed
30 31
from __future__ import print_function

32
import subprocess
Bryton Lacquement's avatar
Bryton Lacquement committed
33
from six.moves.urllib.parse import urlparse
34 35 36
from time import sleep
import glob
import os
37 38
import netifaces
import socket
Alain Takoudjou's avatar
Alain Takoudjou committed
39
from netaddr import valid_ipv4, valid_ipv6
40
from slapos.cli.command import check_root_user
41 42
from slapos.cli.entry import SlapOSApp
from slapos.cli.config import ConfigCommand
43
from slapos.format import isGlobalScopeAddress
44 45 46
from slapos.util import string_to_boolean
import argparse

47

48
def _removeTimestamp(instancehome, partition_base_name):
49
    """
50
    Remove .timestamp from all partitions
51
    """
52 53 54 55
    timestamp_glob_path = os.path.join(
        instancehome,
        "%s*" % partition_base_name,
        ".timestamp")
56
    for timestamp_path in glob.glob(timestamp_glob_path):
Bryton Lacquement's avatar
Bryton Lacquement committed
57
       print("Removing %s" % timestamp_path)
58 59
       os.remove(timestamp_path)

60

61 62 63 64
def _runBang(app):
    """
    Launch slapos node format.
    """
Bryton Lacquement's avatar
Bryton Lacquement committed
65
    print("[BOOT] Invoking slapos node bang...")
66 67 68 69 70
    result = app.run(['node', 'bang', '-m', 'Reboot'])
    if result == 1:
      return 0
    return 1

71

72 73 74 75
def _runFormat(app):
    """
    Launch slapos node format.
    """
Bryton Lacquement's avatar
Bryton Lacquement committed
76
    print("[BOOT] Invoking slapos node format...")
77 78 79 80 81
    result = app.run(['node', 'format', '--now', '--verbose'])
    if result == 1:
      return 0
    return 1

82

83
def _ping(hostname):
84
    """
85 86
    Ping a hostname
    """
Bryton Lacquement's avatar
Bryton Lacquement committed
87
    print("[BOOT] Invoking ipv4 ping to %s..." % hostname)
88 89 90
    p = subprocess.Popen(["ping", "-c", "2", hostname],
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
91 92
    stdout, stderr = p.communicate()
    if p.returncode == 0:
Bryton Lacquement's avatar
Bryton Lacquement committed
93
      print("[BOOT] IPv4 network reachable...")
94
      return 1
Bryton Lacquement's avatar
Bryton Lacquement committed
95
    print("[BOOT] [ERROR] IPv4 network unreachable...")
96 97
    return 0

98

99
def _ping6(hostname):
100
    """
101 102
    Ping an ipv6 address
    """
Bryton Lacquement's avatar
Bryton Lacquement committed
103
    print("[BOOT] Invoking ipv6 ping to %s..." % hostname)
104
    p = subprocess.Popen(
105
        ["ping6", "-c", "2", hostname],
106 107 108
        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = p.communicate()
    if p.returncode == 0:
Bryton Lacquement's avatar
Bryton Lacquement committed
109
        print("[BOOT] IPv6 network reachable...")
110
        return 1
Bryton Lacquement's avatar
Bryton Lacquement committed
111
    print("[BOOT] [ERROR] IPv6 network unreachable...")
112 113
    return 0

114

115 116 117 118 119 120
def _test_ping(hostname):
  is_ready = _ping(hostname)
  while is_ready == 0:
    sleep(5)
    is_ready = _ping(hostname)

121

122 123 124 125 126 127
def _test_ping6(hostname):
  is_ready = _ping6(hostname)
  while is_ready == 0:
    sleep(5)
    is_ready = _ping6(hostname)

128

Alain Takoudjou's avatar
Alain Takoudjou committed
129
def _ping_hostname(hostname):
130 131 132 133 134 135 136 137 138
  is_ready = _ping6(hostname)
  while is_ready == 0:
    sleep(5)
    # Try ping on ipv4
    is_ready = _ping(hostname)
    if is_ready == 0:
      # try ping on ipv6
      is_ready = _ping6(hostname)

139

140 141 142 143 144
def _waitIpv6Ready(ipv6_interface):
  """
    test if ipv6 is ready on ipv6_interface
  """
  ipv6_address = ""
Bryton Lacquement's avatar
Bryton Lacquement committed
145
  print("[BOOT] Checking if %r has IPv6..." % ipv6_interface)
146 147 148 149 150 151 152
  while ipv6_address == "":
    for inet_dict in netifaces.ifaddresses(ipv6_interface)[socket.AF_INET6]:
      ipv6_address = inet_dict['addr'].split('%')[0]
      if isGlobalScopeAddress(ipv6_address):
        break
    else:
      ipv6_address = ""
Bryton Lacquement's avatar
Bryton Lacquement committed
153
      print("[BOOT] [ERROR] No IPv6 found on interface %r, "
154
            "try again in 5 seconds..." % ipv6_interface)
155 156
      sleep(5)

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
class BootCommand(ConfigCommand):
    """
    Test network and invoke simple format and bang (Use on Linux startup)
    """
    command_group = 'node'

    def get_parser(self, prog_name):
        ap = super(BootCommand, self).get_parser(prog_name)
        ap.add_argument('-m', '--message',
                        default="Reboot",
                        help='Message for bang')
        return ap

    def take_action(self, args):
        configp = self.fetch_config(args)
        instance_root = configp.get('slapos','instance_root')
173 174 175
        partition_base_name = "slappart"
        if configp.has_option('slapformat', 'partition_base_name'):
          partition_base_name = configp.get('slapformat', 'partition_base_name')
Bryton Lacquement's avatar
Bryton Lacquement committed
176
        master_url = urlparse(configp.get('slapos','master_url'))
177
        master_hostname = master_url.hostname
178

179 180 181 182 183 184 185
        root_check = True
        if configp.has_option('slapos', 'root_check'):
            root_check = configp.getboolean('slapos', 'root_check')

        if root_check:
            check_root_user(self)

186
        # Check that we have IPv6 ready
187 188
        if configp.has_option('slapformat', 'ipv6_interface'):
            ipv6_interface = configp.get('slapformat', 'ipv6_interface')
189
        elif configp.has_option('slapformat', 'interface_name'):
190
            ipv6_interface = configp.get('slapformat', 'interface_name')
191 192 193 194 195 196 197
        else:
            # It is most likely the we are running on unpriviledged environment
            # so we for slapformat handle it.
            ipv6_interface = None

        if ipv6_interface is not None:
            _waitIpv6Ready(ipv6_interface)
198

199
        # Check that node can ping master
Alain Takoudjou's avatar
Alain Takoudjou committed
200
        if valid_ipv4(master_hostname):
201
            _test_ping(master_hostname)
Alain Takoudjou's avatar
Alain Takoudjou committed
202
        elif valid_ipv6(master_hostname):
203
            _test_ping6(master_hostname)
204
        else:
205 206
            # hostname
            _ping_hostname(master_hostname)
207

208
        app = SlapOSApp()
209
        # Make sure slapos node format returns ok
210
        while not _runFormat(app):
Bryton Lacquement's avatar
Bryton Lacquement committed
211
            print("[BOOT] [ERROR] Fail to format, try again in 15 seconds...")
212
            sleep(15)
213

214
        # Make sure slapos node bang returns ok
215
        while not _runBang(app):
Bryton Lacquement's avatar
Bryton Lacquement committed
216
            print("[BOOT] [ERROR] Fail to bang, try again in 15 seconds...")
217 218
            sleep(15)

219
        _removeTimestamp(instance_root, partition_base_name)