install.py 5.81 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
import salt.client
from salt import config
from salt.log.setup import setup_console_logger
from os.path import join, abspath, dirname
from netifaces import ifaddresses, gateways, AF_INET
from netaddr import IPNetwork
import socket
import yaml
import random
import os
import getpass
from halo import Halo
13
import argparse
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80


PREFIX = dirname(__file__)


def get_timezone():
    localtime = '/etc/localtime'
    try:
        zonefile = abspath(os.readlink(localtime))
        zone_parts = zonefile.split('/')
        return join(zone_parts[-2], zone_parts[-1])
    except Exception:
        return 'Europe/Budapest'


def get_gateway():
    return gateways()['default'][AF_INET]


def get_default_gw():
    return get_gateway()[0]


def get_interface():
    return get_gateway()[1]


def get_ip_with_mask(intf):
    ip = ifaddresses(intf)[AF_INET][0]
    return str(IPNetwork(join(ip['addr'], ip['netmask'])))


def get_hostname():
    return str(socket.gethostname().split('.')[0])


def print_warning(text):
    RED_UNDERLINED = '\033[4;31m'
    NC = '\033[0m'  # No Color
    print(RED_UNDERLINED + text + NC)


def input_password_with_retype():
    pw = getpass.getpass("Enter admin password:")
    if len(pw) == 0:
        print_warning('Please enter a non-empty password!')
        return ('', False)
    pw2 = getpass.getpass("Retype password:")
    status = pw == pw2
    if not status:
        print_warning('The passwords are different.')
    return (pw, status)


def input_admin_password():
    pw, status = input_password_with_retype()
    while not status:
        pw, status = input_password_with_retype()
    return pw.encode('utf8')


def yaml_pretty_dump(data, file, **extra):
    yaml.dump(data, file, encoding='utf-8', default_flow_style=False, **extra)


def dump_errors(result):
    # Filter errors only
81
    errors = {}
82 83
    for key, data in result.iteritems():
        if not data['result']:
84
            errors[key] = data
85
    with open(join(PREFIX, 'errors.yml'), 'w') as f:
86
        yaml_pretty_dump(errors, f)
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114


class KeyStore:
    """ Loads, stores, generates, and saves secret keys """
    def __init__(self, keyfile):
        self.keyfile = keyfile
        self.data = {}
        try:
            with open(keyfile) as f:
                self.data = yaml.safe_load(f)
        except Exception:
            pass

    def gen_key(self, length):
        s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        return "".join(random.sample(s, length))

    def get_key(self, name):
        key = self.data.get(name)
        if key is None:
            key = self.gen_key(16)
            self.data[name] = key
        return key

    def save(self):
        with open(self.keyfile, 'w') as f:
            yaml.dump(self.data, f)

115 116 117
parser = argparse.ArgumentParser()
parser.add_argument('--kvm-present', action='store_true',
                    help='Installs with KVM hypervisor otherwise with QEMU.')
118 119 120 121
parser.add_argument('--dev', action='store_true',
                    help='Installs Develpment version')
parser.add_argument('--local', action='store_true',
                    help='Installs Develpment version')
122
args = parser.parse_args()
123

124 125 126 127 128
if args.dev or args.local:
    deployment_type = 'local'
else:
    deployment_type = 'production'

129
KEYFILE = join(PREFIX, '.circlekeys')
130 131 132 133 134 135 136
ks = KeyStore(KEYFILE)

installer_sls = {
    'user': 'cloud',
    'proxy_secret': ks.get_key('proxy_secret'),
    'secret_key': ks.get_key('secret_key'),
    'timezone': get_timezone(),
137
    'deployment_type': deployment_type,
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    'admin_user': 'admin',
    'admin_pass': input_admin_password(),
    'database': {
        'name': 'circle',
        'user': 'circle',
        'password': ks.get_key('database_password'),
    },
    'amqp': {
        'user': 'cloud',
        'password': ks.get_key('amqp_password'),
        'host': '127.0.0.1',
        'port': 5672,
        'vhost': 'circle',
    },
    'graphite': {
        'user': 'monitor',
        'password': ks.get_key('graphite_password'),
        'host': '127.0.0.1',
        'port': 5672,
        'vhost': 'monitor',
        'queue': 'monitor',
        'secret_key': ks.get_key('graphite_secret_key'),
    },
    'cache': 'pylibmc://127.0.0.1:11211/',
    'nfs': {
        'enabled': True,
        'server': '127.0.0.1',
        'network': '127.0.0.0/8',
        'directory': '/datastore',
    },
    'storagedriver': {
        'queue_name': get_hostname(),
    },
    'fwdriver': {
        'gateway': get_default_gw().encode('utf-8'),
        'external_if': get_interface().encode('utf-8'),
        'external_net':  get_ip_with_mask(get_interface()).encode('utf-8'),
        'queue_name':  get_hostname(),
        'management_if': 'ethy',
        'trunk_if': 'linkb',
    },
179 180 181
    'vmdriver': {
        'hypervisor_type': 'kvm' if args.kvm_present else 'qemu',
    },
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
}

ks.save()  # Save secret keys

# Make installer.sls
INSTALLERT_SLS = join(PREFIX, 'pillar/installer.sls')
with open(INSTALLERT_SLS, 'w') as f:
    yaml_pretty_dump(installer_sls, f)

# NOTE: default logfile is '/var/log/salt/minion'
opts = config.minion_config('')
opts['file_client'] = 'local'
# NOTE: False will cause salt to only display output
#       for states that failed or states that have changes
opts['state_verbose'] = False
opts['file_roots'] = {'base': [join(PREFIX, 'salt')]}
opts['pillar_roots'] = {'base': [join(PREFIX, 'pillar')]}
setup_console_logger(log_level='info')
caller = salt.client.Caller(mopts=opts)
# Run install with salt
with Halo(text='Installing', spinner='dots'):
    result = caller.function('state.sls', 'allinone', with_grains=True)

# Count errors and print to console
error_num = 0
for key, data in result.iteritems():
    if not data['result']:
        print('Error in state: %s' % key)
        error_num += 1

if error_num == 0:
    print('Succesfully installed!')
else:
    print_warning('%i error occured during install!' % error_num)
    dump_errors(result)