Change-Id: I27fc8cd53b8cea2700b8d4ed47a78f71ce4b9469
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/4325
Tested-by: gerrit-photon <photon-checkins@vmware.com>
Reviewed-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Anish Swaminathan <anishs@vmware.com>
| ... | ... |
@@ -1,9 +1,9 @@ |
| 1 | 1 |
{
|
| 2 | 2 |
"packages": ["glibc","zlib","filesystem","file","gmp","libgcc","libstdc++","bash","sed", |
| 3 |
- "bzip2","pkg-config","ncurses","ncurses-compat","cracklib","cracklib-dicts","cracklib-python", |
|
| 3 |
+ "bzip2","pkg-config","python3-curses","ncurses", "cracklib","cracklib-dicts","python3-cracklib", |
|
| 4 | 4 |
"shadow","coreutils","grep","readline","findutils","xz","util-linux","e2fsprogs", |
| 5 | 5 |
"libffi","expat","linux","cpio","Linux-PAM","attr","libcap", "systemd","dbus", |
| 6 | 6 |
"gzip", "sqlite","nspr","nss","popt","lua","rpm","gptfdisk","tar", "librepo", |
| 7 |
- "hawkey", "python2","python2-libs","pcre","glib", "tdnf", "python-requests", |
|
| 7 |
+ "hawkey", "python3","python3-libs","pcre","glib", "tdnf", "python3-requests", |
|
| 8 | 8 |
"grub2-pc", "grub2-efi", "dracut", "curl", "ostree", "dosfstools"] |
| 9 | 9 |
} |
| ... | ... |
@@ -19,7 +19,7 @@ class ConfirmWindow(Window): |
| 19 | 19 |
('No', self.exit_function, False)
|
| 20 | 20 |
] |
| 21 | 21 |
self.menu = Menu(menu_starty, maxx, items, can_navigate_outside = False, horizontal=True) |
| 22 |
- super(ConfirmWindow, self).__init__(height, width, maxy, maxx, 'Confirm', False, self.menu, items=[]) |
|
| 22 |
+ super(ConfirmWindow, self).__init__(height, width, maxy, maxx, 'Confirm', False, self.menu) |
|
| 23 | 23 |
self.addstr(0,0, message) |
| 24 | 24 |
|
| 25 | 25 |
def exit_function(self, yes): |
| ... | ... |
@@ -29,15 +29,16 @@ class Device(object): |
| 29 | 29 |
for deviceline in deviceslines: |
| 30 | 30 |
cols = deviceline.split(None, 2) |
| 31 | 31 |
#skip Virtual NVDIMM from install list |
| 32 |
- if(cols[0].startswith("pmem")):
|
|
| 32 |
+ colstr = cols[0].decode() |
|
| 33 |
+ if(colstr.startswith("pmem")):
|
|
| 33 | 34 |
continue |
| 34 | 35 |
model = "Unknown" |
| 35 | 36 |
if(len(cols) >= 3): |
| 36 |
- model = cols[2] |
|
| 37 |
+ model = cols[2].decode() |
|
| 37 | 38 |
devices.append( |
| 38 | 39 |
Device(model #Model |
| 39 |
- , '/dev/' + cols[0] #Path |
|
| 40 |
- , cols[1] #size |
|
| 40 |
+ , '/dev/' + cols[0].decode() #Path |
|
| 41 |
+ , cols[1].decode() #size |
|
| 41 | 42 |
)) |
| 42 | 43 |
|
| 43 |
- return devices |
|
| 44 | 44 |
\ No newline at end of file |
| 45 |
+ return devices |
| ... | ... |
@@ -53,7 +53,7 @@ class Installer(object): |
| 53 | 53 |
self.progress_width = self.width - self.progress_padding |
| 54 | 54 |
self.starty = (self.maxy - self.height) // 2 |
| 55 | 55 |
self.startx = (self.maxx - self.width) // 2 |
| 56 |
- self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False, items =[]) |
|
| 56 |
+ self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) |
|
| 57 | 57 |
self.progress_bar = ProgressBar(self.starty + 3, self.startx + self.progress_padding // 2, self.progress_width) |
| 58 | 58 |
|
| 59 | 59 |
signal.signal(signal.SIGINT, self.exit_gracefully) |
| ... | ... |
@@ -115,7 +115,7 @@ class Installer(object): |
| 115 | 115 |
process = subprocess.Popen( |
| 116 | 116 |
['tdnf', 'install'] + selected_packages + ['--installroot', self.photon_root, '--nogpgcheck', '--assumeyes'], stdout=subprocess.PIPE, stderr=tdnf_errlog) |
| 117 | 117 |
while True: |
| 118 |
- output = process.stdout.readline() |
|
| 118 |
+ output = process.stdout.readline().decode() |
|
| 119 | 119 |
if output == '': |
| 120 | 120 |
retval = process.poll() |
| 121 | 121 |
if retval is not None: |
| ... | ... |
@@ -404,6 +404,7 @@ class Installer(object): |
| 404 | 404 |
return process.wait() |
| 405 | 405 |
|
| 406 | 406 |
def execute_modules(self, phase): |
| 407 |
+ sys.path.append("./modules")
|
|
| 407 | 408 |
modules_paths = glob.glob('modules/m_*.py')
|
| 408 | 409 |
for mod_path in modules_paths: |
| 409 | 410 |
module = mod_path.replace('/', '.', 1)
|
| ... | ... |
@@ -429,6 +430,7 @@ class Installer(object): |
| 429 | 429 |
if not hasattr(mod, 'execute'): |
| 430 | 430 |
modules.commons.log(modules.commons.LOG_ERROR, "Error: not able to execute module {}".format(module))
|
| 431 | 431 |
continue |
| 432 |
+ |
|
| 432 | 433 |
mod.execute(module, self.install_config, self.photon_root) |
| 433 | 434 |
|
| 434 | 435 |
def adjust_packages_for_vmware_virt(self): |
| ... | ... |
@@ -446,18 +448,3 @@ class Installer(object): |
| 446 | 446 |
selected_packages.append('linux-esx')
|
| 447 | 447 |
except KeyError: |
| 448 | 448 |
pass |
| 449 |
- |
|
| 450 |
- def run(self, command, comment = None): |
|
| 451 |
- if comment != None: |
|
| 452 |
- modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(comment))
|
|
| 453 |
- self.progress_bar.update_loading_message(comment) |
|
| 454 |
- |
|
| 455 |
- modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(command))
|
|
| 456 |
- process = subprocess.Popen([command], shell=True, stdout=subprocess.PIPE) |
|
| 457 |
- out,err = process.communicate() |
|
| 458 |
- if err != None and err != 0 and "systemd-tmpfiles" not in command: |
|
| 459 |
- modules.commons.log(modules.commons.LOG_ERROR, "Installer: failed in {} with error code {}".format(command, err))
|
|
| 460 |
- modules.commons.log(modules.commons.LOG_ERROR, out) |
|
| 461 |
- self.exit_gracefully(None, None) |
|
| 462 |
- |
|
| 463 |
- return err |
| ... | ... |
@@ -1,298 +1,15 @@ |
| 1 |
-#! /usr/bin/python2 |
|
| 1 |
+#! /usr/bin/python3 |
|
| 2 | 2 |
# |
| 3 | 3 |
# Copyright (C) 2015 vmware inc. |
| 4 | 4 |
# |
| 5 | 5 |
# Author: Mahmoud Bassiouny <mbassiouny@vmware.com> |
| 6 | 6 |
|
| 7 | 7 |
from argparse import ArgumentParser |
| 8 |
-import os.path |
|
| 9 | 8 |
import curses |
| 10 |
-import subprocess |
|
| 11 |
-import re |
|
| 12 |
-import json |
|
| 13 |
-import time |
|
| 14 |
-import crypt |
|
| 15 |
-import string |
|
| 16 |
-import random |
|
| 17 |
-import urllib2 |
|
| 18 |
-import requests |
|
| 19 |
-import cracklib |
|
| 20 |
-import modules.commons |
|
| 21 |
-from partitionISO import PartitionISO |
|
| 22 |
-from packageselector import PackageSelector |
|
| 23 |
-from installer import Installer |
|
| 24 | 9 |
from installercontainer import InstallerContainer |
| 25 |
-from windowstringreader import WindowStringReader |
|
| 26 |
-from jsonwrapper import JsonWrapper |
|
| 27 |
-from selectdisk import SelectDisk |
|
| 28 |
-from license import License |
|
| 29 |
-from linuxselector import LinuxSelector |
|
| 10 |
+from iso_config import IsoConfig |
|
| 30 | 11 |
|
| 31 | 12 |
class IsoInstaller(object): |
| 32 |
- |
|
| 33 |
- def get_config(self, path): |
|
| 34 |
- if path.startswith("http://"):
|
|
| 35 |
- # Do 5 trials to get the kick start |
|
| 36 |
- # TODO: make sure the installer run after network is up |
|
| 37 |
- ks_file_error = "Failed to get the kickstart file at {0}".format(path)
|
|
| 38 |
- wait = 1 |
|
| 39 |
- for x in range(0, 5): |
|
| 40 |
- err_msg = "" |
|
| 41 |
- try: |
|
| 42 |
- response = requests.get(path, timeout=3) |
|
| 43 |
- if response.ok: |
|
| 44 |
- return json.loads(response.text) |
|
| 45 |
- err_msg = response.text |
|
| 46 |
- except Exception as e: |
|
| 47 |
- err_msg = e |
|
| 48 |
- |
|
| 49 |
- modules.commons.log(modules.commons.LOG_ERROR, |
|
| 50 |
- ks_file_error) |
|
| 51 |
- modules.commons.log(modules.commons.LOG_ERROR, |
|
| 52 |
- "error msg: {0}".format(err_msg))
|
|
| 53 |
- print(ks_file_error) |
|
| 54 |
- print("retry in a second")
|
|
| 55 |
- time.sleep(wait) |
|
| 56 |
- wait = wait * 2 |
|
| 57 |
- |
|
| 58 |
- # Something went wrong |
|
| 59 |
- print(ks_file_error) |
|
| 60 |
- print("exiting the installer, check the logs for more details")
|
|
| 61 |
- raise Exception(err_msg) |
|
| 62 |
- else: |
|
| 63 |
- if path.startswith("cdrom:/"):
|
|
| 64 |
- self.mount_RPMS_cd() |
|
| 65 |
- path = os.path.join(self.cd_path, path.replace("cdrom:/", "", 1))
|
|
| 66 |
- return (JsonWrapper(path)).read() |
|
| 67 |
- |
|
| 68 |
- def mount_RPMS_cd(self): |
|
| 69 |
- # check if the cd is already mounted |
|
| 70 |
- if self.cd_path: |
|
| 71 |
- return |
|
| 72 |
- |
|
| 73 |
- # Mount the cd to get the RPMS |
|
| 74 |
- process = subprocess.Popen(['mkdir', '-p', '/mnt/cdrom']) |
|
| 75 |
- retval = process.wait() |
|
| 76 |
- |
|
| 77 |
- # Retry mount the CD |
|
| 78 |
- for i in range(0, 3): |
|
| 79 |
- process = subprocess.Popen(['mount', '/dev/cdrom', '/mnt/cdrom']) |
|
| 80 |
- retval = process.wait() |
|
| 81 |
- if retval == 0: |
|
| 82 |
- self.cd_path = "/mnt/cdrom" |
|
| 83 |
- return |
|
| 84 |
- print("Failed to mount the cd, retry in a second")
|
|
| 85 |
- time.sleep(1) |
|
| 86 |
- print("Failed to mount the cd, exiting the installer")
|
|
| 87 |
- print("check the logs for more details")
|
|
| 88 |
- raise Exception("Can not mount the cd")
|
|
| 89 |
- |
|
| 90 |
- def validate_hostname(self, hostname): |
|
| 91 |
- error_empty = "Empty hostname or domain is not allowed" |
|
| 92 |
- error_dash = "Hostname or domain should not start or end with '-'" |
|
| 93 |
- error_hostname = "Hostname should start with alpha char and <= 64 chars" |
|
| 94 |
- |
|
| 95 |
- if hostname is None or len(hostname) == 0: |
|
| 96 |
- return False, error_empty |
|
| 97 |
- |
|
| 98 |
- fields = hostname.split('.')
|
|
| 99 |
- for field in fields: |
|
| 100 |
- if len(field) == 0: |
|
| 101 |
- return False, error_empty |
|
| 102 |
- if field[0] == '-' or field[-1] == '-': |
|
| 103 |
- return False, error_dash |
|
| 104 |
- |
|
| 105 |
- machinename = fields[0] |
|
| 106 |
- return (len(machinename) <= 64) and (ord(machinename[0]) in self.alpha_chars), error_hostname |
|
| 107 |
- |
|
| 108 |
- @staticmethod |
|
| 109 |
- def validate_password(text): |
|
| 110 |
- try: |
|
| 111 |
- p = cracklib.VeryFascistCheck(text) |
|
| 112 |
- except ValueError as message: |
|
| 113 |
- p = str(message) |
|
| 114 |
- return p == text, "Error: " + p |
|
| 115 |
- |
|
| 116 |
- @staticmethod |
|
| 117 |
- def generate_password_hash(password): |
|
| 118 |
- shadow_password = crypt.crypt(password, "$6$" + "".join([random.choice(string.ascii_letters + string.digits) for _ in range(16)])) |
|
| 119 |
- return shadow_password |
|
| 120 |
- |
|
| 121 |
- def validate_http_response(self, url, checks, exception_text, error_text): |
|
| 122 |
- try: |
|
| 123 |
- if url.startswith("https"):
|
|
| 124 |
- response = urllib2.urlopen(url, cafile="/usr/lib/python2.7/site-packages/requests/cacert.pem") |
|
| 125 |
- else: |
|
| 126 |
- response = urllib2.urlopen(url) |
|
| 127 |
- |
|
| 128 |
- except: |
|
| 129 |
- return exception_text |
|
| 130 |
- else: |
|
| 131 |
- if response.getcode() != 200: |
|
| 132 |
- return error_text |
|
| 133 |
- |
|
| 134 |
- html = response.read() |
|
| 135 |
- |
|
| 136 |
- for pattern, count, failed_check_text in checks: |
|
| 137 |
- match = re.findall(pattern, html) |
|
| 138 |
- if len(match) != count: |
|
| 139 |
- return failed_check_text |
|
| 140 |
- |
|
| 141 |
- return "" |
|
| 142 |
- def ui_install(self, options_file, rpm_path): |
|
| 143 |
- # This represents the installer screen, the bool indicated if I can go back to this window or not |
|
| 144 |
- items = [] |
|
| 145 |
- random_id = '%12x' % random.randrange(16**12) |
|
| 146 |
- random_hostname = "photon-" + random_id.strip() |
|
| 147 |
- install_config = {'iso_system': False}
|
|
| 148 |
- install_config['ui_install'] = True |
|
| 149 |
- license_agreement = License(self.maxy, self.maxx) |
|
| 150 |
- select_disk = SelectDisk(self.maxy, self.maxx, install_config) |
|
| 151 |
- select_partition = PartitionISO(self.maxy, self.maxx, install_config) |
|
| 152 |
- package_selector = PackageSelector(self.maxy, self.maxx, install_config, options_file) |
|
| 153 |
- self.alpha_chars = range(65, 91) |
|
| 154 |
- self.alpha_chars.extend(range(97, 123)) |
|
| 155 |
- hostname_accepted_chars = list(self.alpha_chars) |
|
| 156 |
- # Adding the numeric chars |
|
| 157 |
- hostname_accepted_chars.extend(range(48, 58)) |
|
| 158 |
- # Adding the . and - |
|
| 159 |
- hostname_accepted_chars.extend([ord('.'), ord('-')])
|
|
| 160 |
- |
|
| 161 |
- hostname_reader = WindowStringReader( |
|
| 162 |
- self.maxy, self.maxx, 10, 70, |
|
| 163 |
- 'hostname', |
|
| 164 |
- None, # confirmation error msg if it's a confirmation text |
|
| 165 |
- None, # echo char |
|
| 166 |
- hostname_accepted_chars, # set of accepted chars |
|
| 167 |
- self.validate_hostname, # validation function of the input |
|
| 168 |
- None, # post processing of the input field |
|
| 169 |
- 'Choose the hostname for your system', 'Hostname:', 2, install_config, |
|
| 170 |
- random_hostname, |
|
| 171 |
- True) |
|
| 172 |
- root_password_reader = WindowStringReader( |
|
| 173 |
- self.maxy, self.maxx, 10, 70, |
|
| 174 |
- 'password', |
|
| 175 |
- None, # confirmation error msg if it's a confirmation text |
|
| 176 |
- '*', # echo char |
|
| 177 |
- None, # set of accepted chars |
|
| 178 |
- IsoInstaller.validate_password, # validation function of the input |
|
| 179 |
- None, # post processing of the input field |
|
| 180 |
- 'Set up root password', 'Root password:', 2, install_config) |
|
| 181 |
- confirm_password_reader = WindowStringReader( |
|
| 182 |
- self.maxy, self.maxx, 10, 70, |
|
| 183 |
- 'password', |
|
| 184 |
- "Passwords don't match, please try again.", # confirmation error msg if it's a confirmation text |
|
| 185 |
- '*', # echo char |
|
| 186 |
- None, # set of accepted chars |
|
| 187 |
- None, # validation function of the input |
|
| 188 |
- IsoInstaller.generate_password_hash, # post processing of the input field |
|
| 189 |
- 'Confirm root password', 'Confirm Root password:', 2, install_config) |
|
| 190 |
- |
|
| 191 |
- items.append((license_agreement.display, False)) |
|
| 192 |
- items.append((select_disk.display, True)) |
|
| 193 |
- items.append((select_partition.display, False)) |
|
| 194 |
- items.append((select_disk.guided_partitions, False)) |
|
| 195 |
- items.append((package_selector.display, True)) |
|
| 196 |
- select_linux_index = -1 |
|
| 197 |
- if self.is_vmware_virtualization(): |
|
| 198 |
- linux_selector = LinuxSelector(self.maxy, self.maxx, install_config) |
|
| 199 |
- items.append((linux_selector.display, True)) |
|
| 200 |
- select_linux_index = items.index((linux_selector.display, True)) |
|
| 201 |
- items.append((hostname_reader.get_user_string, True)) |
|
| 202 |
- items.append((root_password_reader.get_user_string, True)) |
|
| 203 |
- items.append((confirm_password_reader.get_user_string, False)) |
|
| 204 |
- installer = InstallerContainer( |
|
| 205 |
- install_config, |
|
| 206 |
- self.maxy, |
|
| 207 |
- self.maxx, |
|
| 208 |
- True, |
|
| 209 |
- rpm_path=rpm_path, |
|
| 210 |
- log_path="/var/log") |
|
| 211 |
- |
|
| 212 |
- items = items + [(installer.install, False)] |
|
| 213 |
- |
|
| 214 |
- index = 0 |
|
| 215 |
- params = None |
|
| 216 |
- while True: |
|
| 217 |
- result = items[index][0](params) |
|
| 218 |
- if result.success: |
|
| 219 |
- index += 1 |
|
| 220 |
- params = result.result |
|
| 221 |
- if index == len(items) - 1: |
|
| 222 |
- self.screen.clear() |
|
| 223 |
- if index == len(items): |
|
| 224 |
- break |
|
| 225 |
- #Skip linux select screen for ostree installation. |
|
| 226 |
- if index == select_linux_index: |
|
| 227 |
- if install_config['type'] == 'ostree_server': |
|
| 228 |
- index += 1 |
|
| 229 |
- else: |
|
| 230 |
- index -= 1 |
|
| 231 |
- while index >= 0 and items[index][1] is False: |
|
| 232 |
- index -= 1 |
|
| 233 |
- if index < 0: |
|
| 234 |
- index = 0 |
|
| 235 |
- #Skip linux select screen for ostree installation. |
|
| 236 |
- if index == select_linux_index: |
|
| 237 |
- if install_config['type'] == 'ostree_server': |
|
| 238 |
- index -= 1 |
|
| 239 |
- |
|
| 240 |
- def ks_install(self, options_file, rpm_path, ks_config): |
|
| 241 |
- install_config = ks_config |
|
| 242 |
- install_config['iso_system'] = False |
|
| 243 |
- if self.is_vmware_virtualization() and 'install_linux_esx' not in install_config: |
|
| 244 |
- install_config['install_linux_esx'] = True |
|
| 245 |
- |
|
| 246 |
- json_wrapper_option_list = JsonWrapper("build_install_options_all.json")
|
|
| 247 |
- option_list_json = json_wrapper_option_list.read() |
|
| 248 |
- options_sorted = option_list_json.items() |
|
| 249 |
- |
|
| 250 |
- base_path = os.path.dirname("build_install_options_all.json")
|
|
| 251 |
- package_list = [] |
|
| 252 |
- |
|
| 253 |
- package_list = PackageSelector.get_packages_to_install(options_sorted, install_config['type'], base_path) |
|
| 254 |
- if 'additional_packages' in install_config: |
|
| 255 |
- package_list.extend(install_config['additional_packages']) |
|
| 256 |
- install_config['packages'] = package_list |
|
| 257 |
- |
|
| 258 |
- if 'partitions' in install_config: |
|
| 259 |
- partitions = install_config['partitions'] |
|
| 260 |
- else: |
|
| 261 |
- partitions = modules.commons.default_partitions |
|
| 262 |
- |
|
| 263 |
- install_config['disk'] = modules.commons.partition_disk(install_config['disk'], partitions) |
|
| 264 |
- |
|
| 265 |
- if "hostname" in install_config: |
|
| 266 |
- evalhostname = os.popen('printf ' + install_config["hostname"].strip(" ")).readlines()
|
|
| 267 |
- install_config['hostname'] = evalhostname[0] |
|
| 268 |
- if "hostname" not in install_config or install_config['hostname'] == "": |
|
| 269 |
- random_id = '%12x' % random.randrange(16**12) |
|
| 270 |
- install_config['hostname'] = "photon-" + random_id.strip() |
|
| 271 |
- |
|
| 272 |
- # crypt the password if needed |
|
| 273 |
- if install_config['password']['crypted']: |
|
| 274 |
- install_config['password'] = install_config['password']['text'] |
|
| 275 |
- else: |
|
| 276 |
- install_config['password'] = crypt.crypt(install_config['password']['text'], |
|
| 277 |
- "$6$" + "".join([random.choice(string.ascii_letters + string.digits) for _ in range(16)])) |
|
| 278 |
- |
|
| 279 |
- installer = InstallerContainer( |
|
| 280 |
- install_config, |
|
| 281 |
- self.maxy, self.maxx, |
|
| 282 |
- True, |
|
| 283 |
- rpm_path=rpm_path, |
|
| 284 |
- log_path="/var/log") |
|
| 285 |
- |
|
| 286 |
- installer.install(None) |
|
| 287 |
- |
|
| 288 |
- def is_vmware_virtualization(self): |
|
| 289 |
- process = subprocess.Popen(['systemd-detect-virt'], stdout=subprocess.PIPE) |
|
| 290 |
- out, err = process.communicate() |
|
| 291 |
- if err is not None and err != 0: |
|
| 292 |
- return False |
|
| 293 |
- else: |
|
| 294 |
- return out == 'vmware\n' |
|
| 295 |
- |
|
| 296 | 13 |
def __init__(self, stdscreen, options_file): |
| 297 | 14 |
self.screen = stdscreen |
| 298 | 15 |
|
| ... | ... |
@@ -307,35 +24,22 @@ class IsoInstaller(object): |
| 307 | 307 |
self.maxy, self.maxx = self.screen.getmaxyx() |
| 308 | 308 |
self.screen.addstr(self.maxy - 1, 0, ' Arrow keys make selections; <Enter> activates.') |
| 309 | 309 |
curses.curs_set(0) |
| 310 |
+ config = IsoConfig() |
|
| 311 |
+ rpm_path, install_config = config.Configure(options_file, self.maxy, self.maxx) |
|
| 310 | 312 |
|
| 311 |
- self.cd_path = None |
|
| 312 |
- |
|
| 313 |
- kernel_params = subprocess.check_output(['cat', '/proc/cmdline']) |
|
| 314 |
- |
|
| 315 |
- # check the kickstart param |
|
| 316 |
- ks_config = None |
|
| 317 |
- m = re.match(r".*ks=(\S+)\s*.*\s*", kernel_params) |
|
| 318 |
- if m != None: |
|
| 319 |
- ks_config = self.get_config(m.group(1)) |
|
| 320 |
- |
|
| 321 |
- # check for the repo param |
|
| 322 |
- m = re.match(r".*repo=(\S+)\s*.*\s*", kernel_params) |
|
| 323 |
- if m != None: |
|
| 324 |
- rpm_path = m.group(1) |
|
| 325 |
- else: |
|
| 326 |
- # the rpms should be in the cd |
|
| 327 |
- self.mount_RPMS_cd() |
|
| 328 |
- rpm_path = os.path.join(self.cd_path, "RPMS") |
|
| 313 |
+ self.screen.clear() |
|
| 314 |
+ installer = InstallerContainer( |
|
| 315 |
+ install_config, |
|
| 316 |
+ self.maxy, self.maxx, |
|
| 317 |
+ True, |
|
| 318 |
+ rpm_path=rpm_path, |
|
| 319 |
+ log_path="/var/log") |
|
| 329 | 320 |
|
| 330 |
- if ks_config: |
|
| 331 |
- self.ks_install(options_file, rpm_path, ks_config) |
|
| 332 |
- else: |
|
| 333 |
- self.ui_install(options_file, rpm_path) |
|
| 321 |
+ installer.install(None) |
|
| 334 | 322 |
|
| 335 | 323 |
if __name__ == '__main__': |
| 336 | 324 |
usage = "Usage: %prog [options]" |
| 337 | 325 |
parser = ArgumentParser(usage) |
| 338 | 326 |
parser.add_argument("-j", "--json-file", dest="options_file", default="input.json")
|
| 339 |
- |
|
| 340 | 327 |
options = parser.parse_args() |
| 341 | 328 |
curses.wrapper(IsoInstaller, options.options_file) |
| 342 | 329 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,269 @@ |
| 0 |
+import os.path |
|
| 1 |
+import subprocess |
|
| 2 |
+import re |
|
| 3 |
+import json |
|
| 4 |
+import time |
|
| 5 |
+import crypt |
|
| 6 |
+import string |
|
| 7 |
+import random |
|
| 8 |
+import requests |
|
| 9 |
+import cracklib |
|
| 10 |
+import modules.commons |
|
| 11 |
+from partitionISO import PartitionISO |
|
| 12 |
+from packageselector import PackageSelector |
|
| 13 |
+from windowstringreader import WindowStringReader |
|
| 14 |
+from jsonwrapper import JsonWrapper |
|
| 15 |
+from selectdisk import SelectDisk |
|
| 16 |
+from license import License |
|
| 17 |
+from linuxselector import LinuxSelector |
|
| 18 |
+ |
|
| 19 |
+class IsoConfig(object): |
|
| 20 |
+ def Configure(self,options_file, maxy, maxx): |
|
| 21 |
+ self.cd_path = None |
|
| 22 |
+ |
|
| 23 |
+ kernel_params = subprocess.check_output(['cat', '/proc/cmdline']) |
|
| 24 |
+ |
|
| 25 |
+ # check for the repo param |
|
| 26 |
+ m = re.match(r".*repo=(\S+)\s*.*\s*", kernel_params.decode()) |
|
| 27 |
+ if m != None: |
|
| 28 |
+ rpm_path = m.group(1) |
|
| 29 |
+ else: |
|
| 30 |
+ # the rpms should be in the cd |
|
| 31 |
+ self.mount_RPMS_cd() |
|
| 32 |
+ rpm_path = os.path.join(self.cd_path, "RPMS") |
|
| 33 |
+ |
|
| 34 |
+ # check the kickstart param |
|
| 35 |
+ ks_config = None |
|
| 36 |
+ m = re.match(r".*ks=(\S+)\s*.*\s*", kernel_params.decode()) |
|
| 37 |
+ if m != None: |
|
| 38 |
+ ks_config = self.get_config(m.group(1)) |
|
| 39 |
+ |
|
| 40 |
+ install_config = None |
|
| 41 |
+ if ks_config: |
|
| 42 |
+ install_config = self.ks_config(options_file, ks_config) |
|
| 43 |
+ else: |
|
| 44 |
+ install_config = self.ui_config(options_file, maxy, maxx) |
|
| 45 |
+ return rpm_path, install_config |
|
| 46 |
+ |
|
| 47 |
+ def is_vmware_virtualization(self): |
|
| 48 |
+ process = subprocess.Popen(['systemd-detect-virt'], stdout=subprocess.PIPE) |
|
| 49 |
+ out, err = process.communicate() |
|
| 50 |
+ if err is not None and err != 0: |
|
| 51 |
+ return False |
|
| 52 |
+ else: |
|
| 53 |
+ return out == 'vmware\n' |
|
| 54 |
+ |
|
| 55 |
+ def get_config(self, path): |
|
| 56 |
+ if path.startswith("http://"):
|
|
| 57 |
+ # Do 5 trials to get the kick start |
|
| 58 |
+ # TODO: make sure the installer run after network is up |
|
| 59 |
+ ks_file_error = "Failed to get the kickstart file at {0}".format(path)
|
|
| 60 |
+ wait = 1 |
|
| 61 |
+ for x in range(0, 5): |
|
| 62 |
+ err_msg = "" |
|
| 63 |
+ try: |
|
| 64 |
+ response = requests.get(path, timeout=3) |
|
| 65 |
+ if response.ok: |
|
| 66 |
+ return json.loads(response.text) |
|
| 67 |
+ err_msg = response.text |
|
| 68 |
+ except Exception as e: |
|
| 69 |
+ err_msg = e |
|
| 70 |
+ |
|
| 71 |
+ modules.commons.log(modules.commons.LOG_ERROR, |
|
| 72 |
+ ks_file_error) |
|
| 73 |
+ modules.commons.log(modules.commons.LOG_ERROR, |
|
| 74 |
+ "error msg: {0}".format(err_msg))
|
|
| 75 |
+ print(ks_file_error) |
|
| 76 |
+ print("retry in a second")
|
|
| 77 |
+ time.sleep(wait) |
|
| 78 |
+ wait = wait * 2 |
|
| 79 |
+ |
|
| 80 |
+ # Something went wrong |
|
| 81 |
+ print(ks_file_error) |
|
| 82 |
+ print("exiting the installer, check the logs for more details")
|
|
| 83 |
+ raise Exception(err_msg) |
|
| 84 |
+ else: |
|
| 85 |
+ if path.startswith("cdrom:/"):
|
|
| 86 |
+ self.mount_RPMS_cd() |
|
| 87 |
+ path = os.path.join(self.cd_path, path.replace("cdrom:/", "", 1))
|
|
| 88 |
+ return (JsonWrapper(path)).read() |
|
| 89 |
+ |
|
| 90 |
+ def mount_RPMS_cd(self): |
|
| 91 |
+ # check if the cd is already mounted |
|
| 92 |
+ if self.cd_path: |
|
| 93 |
+ return |
|
| 94 |
+ |
|
| 95 |
+ # Mount the cd to get the RPMS |
|
| 96 |
+ process = subprocess.Popen(['mkdir', '-p', '/mnt/cdrom']) |
|
| 97 |
+ retval = process.wait() |
|
| 98 |
+ |
|
| 99 |
+ # Retry mount the CD |
|
| 100 |
+ for i in range(0, 3): |
|
| 101 |
+ process = subprocess.Popen(['mount', '/dev/cdrom', '/mnt/cdrom']) |
|
| 102 |
+ retval = process.wait() |
|
| 103 |
+ if retval == 0: |
|
| 104 |
+ self.cd_path = "/mnt/cdrom" |
|
| 105 |
+ return |
|
| 106 |
+ print("Failed to mount the cd, retry in a second")
|
|
| 107 |
+ time.sleep(1) |
|
| 108 |
+ print("Failed to mount the cd, exiting the installer")
|
|
| 109 |
+ print("check the logs for more details")
|
|
| 110 |
+ raise Exception("Can not mount the cd")
|
|
| 111 |
+ |
|
| 112 |
+ def ks_config(self, options_file, ks_config): |
|
| 113 |
+ install_config = ks_config |
|
| 114 |
+ install_config['iso_system'] = False |
|
| 115 |
+ if self.is_vmware_virtualization() and 'install_linux_esx' not in install_config: |
|
| 116 |
+ install_config['install_linux_esx'] = True |
|
| 117 |
+ |
|
| 118 |
+ json_wrapper_option_list = JsonWrapper("build_install_options_all.json")
|
|
| 119 |
+ option_list_json = json_wrapper_option_list.read() |
|
| 120 |
+ options_sorted = option_list_json.items() |
|
| 121 |
+ |
|
| 122 |
+ base_path = os.path.dirname("build_install_options_all.json")
|
|
| 123 |
+ package_list = [] |
|
| 124 |
+ |
|
| 125 |
+ package_list = PackageSelector.get_packages_to_install(options_sorted, install_config['type'], base_path) |
|
| 126 |
+ if 'additional_packages' in install_config: |
|
| 127 |
+ package_list.extend(install_config['additional_packages']) |
|
| 128 |
+ install_config['packages'] = package_list |
|
| 129 |
+ |
|
| 130 |
+ if 'partitions' in install_config: |
|
| 131 |
+ partitions = install_config['partitions'] |
|
| 132 |
+ else: |
|
| 133 |
+ partitions = modules.commons.default_partitions |
|
| 134 |
+ |
|
| 135 |
+ install_config['disk'] = modules.commons.partition_disk(install_config['disk'], partitions) |
|
| 136 |
+ |
|
| 137 |
+ if "hostname" in install_config: |
|
| 138 |
+ evalhostname = os.popen('printf ' + install_config["hostname"].strip(" ")).readlines()
|
|
| 139 |
+ install_config['hostname'] = evalhostname[0] |
|
| 140 |
+ if "hostname" not in install_config or install_config['hostname'] == "": |
|
| 141 |
+ random_id = '%12x' % random.randrange(16**12) |
|
| 142 |
+ install_config['hostname'] = "photon-" + random_id.strip() |
|
| 143 |
+ |
|
| 144 |
+ # crypt the password if needed |
|
| 145 |
+ if install_config['password']['crypted']: |
|
| 146 |
+ install_config['password'] = install_config['password']['text'] |
|
| 147 |
+ else: |
|
| 148 |
+ install_config['password'] = crypt.crypt(install_config['password']['text'], |
|
| 149 |
+ "$6$" + "".join([random.choice(string.ascii_letters + string.digits) for _ in range(16)])) |
|
| 150 |
+ return install_config |
|
| 151 |
+ |
|
| 152 |
+ def validate_hostname(self, hostname): |
|
| 153 |
+ error_empty = "Empty hostname or domain is not allowed" |
|
| 154 |
+ error_dash = "Hostname or domain should not start or end with '-'" |
|
| 155 |
+ error_hostname = "Hostname should start with alpha char and <= 64 chars" |
|
| 156 |
+ |
|
| 157 |
+ if hostname is None or len(hostname) == 0: |
|
| 158 |
+ return False, error_empty |
|
| 159 |
+ |
|
| 160 |
+ fields = hostname.split('.')
|
|
| 161 |
+ for field in fields: |
|
| 162 |
+ if len(field) == 0: |
|
| 163 |
+ return False, error_empty |
|
| 164 |
+ if field[0] == '-' or field[-1] == '-': |
|
| 165 |
+ return False, error_dash |
|
| 166 |
+ |
|
| 167 |
+ machinename = fields[0] |
|
| 168 |
+ return (len(machinename) <= 64) and (ord(machinename[0]) in self.alpha_chars), error_hostname |
|
| 169 |
+ |
|
| 170 |
+ @staticmethod |
|
| 171 |
+ def validate_password(text): |
|
| 172 |
+ try: |
|
| 173 |
+ p = cracklib.VeryFascistCheck(text) |
|
| 174 |
+ except ValueError as message: |
|
| 175 |
+ p = str(message) |
|
| 176 |
+ return p == text, "Error: " + p |
|
| 177 |
+ |
|
| 178 |
+ @staticmethod |
|
| 179 |
+ def generate_password_hash(password): |
|
| 180 |
+ shadow_password = crypt.crypt(password, "$6$" + "".join([random.choice(string.ascii_letters + string.digits) for _ in range(16)])) |
|
| 181 |
+ return shadow_password |
|
| 182 |
+ |
|
| 183 |
+ def ui_config(self, options_file, maxy, maxx): |
|
| 184 |
+ # This represents the installer screen, the bool indicated if I can go back to this window or not |
|
| 185 |
+ items = [] |
|
| 186 |
+ random_id = '%12x' % random.randrange(16**12) |
|
| 187 |
+ random_hostname = "photon-" + random_id.strip() |
|
| 188 |
+ install_config = {'iso_system': False}
|
|
| 189 |
+ install_config['ui_install'] = True |
|
| 190 |
+ license_agreement = License(maxy, maxx) |
|
| 191 |
+ select_disk = SelectDisk(maxy, maxx, install_config) |
|
| 192 |
+ select_partition = PartitionISO(maxy, maxx, install_config) |
|
| 193 |
+ package_selector = PackageSelector(maxy, maxx, install_config, options_file) |
|
| 194 |
+ self.alpha_chars = list(range(65, 91)) |
|
| 195 |
+ self.alpha_chars.extend(range(97, 123)) |
|
| 196 |
+ hostname_accepted_chars = self.alpha_chars |
|
| 197 |
+ # Adding the numeric chars |
|
| 198 |
+ hostname_accepted_chars.extend(range(48, 58)) |
|
| 199 |
+ # Adding the . and - |
|
| 200 |
+ hostname_accepted_chars.extend([ord('.'), ord('-')])
|
|
| 201 |
+ |
|
| 202 |
+ hostname_reader = WindowStringReader( |
|
| 203 |
+ maxy, maxx, 10, 70, |
|
| 204 |
+ 'hostname', |
|
| 205 |
+ None, # confirmation error msg if it's a confirmation text |
|
| 206 |
+ None, # echo char |
|
| 207 |
+ hostname_accepted_chars, # set of accepted chars |
|
| 208 |
+ self.validate_hostname, # validation function of the input |
|
| 209 |
+ None, # post processing of the input field |
|
| 210 |
+ 'Choose the hostname for your system', 'Hostname:', 2, install_config, |
|
| 211 |
+ random_hostname, |
|
| 212 |
+ True) |
|
| 213 |
+ root_password_reader = WindowStringReader( |
|
| 214 |
+ maxy, maxx, 10, 70, |
|
| 215 |
+ 'password', |
|
| 216 |
+ None, # confirmation error msg if it's a confirmation text |
|
| 217 |
+ '*', # echo char |
|
| 218 |
+ None, # set of accepted chars |
|
| 219 |
+ IsoConfig.validate_password, # validation function of the input |
|
| 220 |
+ None, # post processing of the input field |
|
| 221 |
+ 'Set up root password', 'Root password:', 2, install_config) |
|
| 222 |
+ confirm_password_reader = WindowStringReader( |
|
| 223 |
+ maxy, maxx, 10, 70, |
|
| 224 |
+ 'password', |
|
| 225 |
+ "Passwords don't match, please try again.", # confirmation error msg if it's a confirmation text |
|
| 226 |
+ '*', # echo char |
|
| 227 |
+ None, # set of accepted chars |
|
| 228 |
+ None, # validation function of the input |
|
| 229 |
+ IsoConfig.generate_password_hash, # post processing of the input field |
|
| 230 |
+ 'Confirm root password', 'Confirm Root password:', 2, install_config) |
|
| 231 |
+ |
|
| 232 |
+ items.append((license_agreement.display, False)) |
|
| 233 |
+ items.append((select_disk.display, True)) |
|
| 234 |
+ items.append((select_partition.display, False)) |
|
| 235 |
+ items.append((select_disk.guided_partitions, False)) |
|
| 236 |
+ items.append((package_selector.display, True)) |
|
| 237 |
+ select_linux_index = -1 |
|
| 238 |
+ if self.is_vmware_virtualization(): |
|
| 239 |
+ linux_selector = LinuxSelector(maxy, maxx, install_config) |
|
| 240 |
+ items.append((linux_selector.display, True)) |
|
| 241 |
+ select_linux_index = items.index((linux_selector.display, True)) |
|
| 242 |
+ items.append((hostname_reader.get_user_string, True)) |
|
| 243 |
+ items.append((root_password_reader.get_user_string, True)) |
|
| 244 |
+ items.append((confirm_password_reader.get_user_string, False)) |
|
| 245 |
+ index = 0 |
|
| 246 |
+ params = None |
|
| 247 |
+ while True: |
|
| 248 |
+ result = items[index][0](params) |
|
| 249 |
+ if result.success: |
|
| 250 |
+ index += 1 |
|
| 251 |
+ params = result.result |
|
| 252 |
+ if index == len(items): |
|
| 253 |
+ break |
|
| 254 |
+ #Skip linux select screen for ostree installation. |
|
| 255 |
+ if index == select_linux_index: |
|
| 256 |
+ if install_config['type'] == 'ostree_server': |
|
| 257 |
+ index += 1 |
|
| 258 |
+ else: |
|
| 259 |
+ index -= 1 |
|
| 260 |
+ while index >= 0 and items[index][1] is False: |
|
| 261 |
+ index -= 1 |
|
| 262 |
+ if index < 0: |
|
| 263 |
+ index = 0 |
|
| 264 |
+ #Skip linux select screen for ostree installation. |
|
| 265 |
+ if index == select_linux_index: |
|
| 266 |
+ if install_config['type'] == 'ostree_server': |
|
| 267 |
+ index -= 1 |
|
| 268 |
+ return install_config |
| ... | ... |
@@ -21,7 +21,7 @@ class License(object): |
| 21 | 21 |
self.text_height = self.win_height - 6 |
| 22 | 22 |
self.text_width = self.win_width - 6 |
| 23 | 23 |
|
| 24 |
- self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False, items=[]) |
|
| 24 |
+ self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False) |
|
| 25 | 25 |
|
| 26 | 26 |
def display(self, params): |
| 27 | 27 |
accept_decline_items = [ |
| ... | ... |
@@ -15,8 +15,8 @@ class LinuxSelector(object): |
| 15 | 15 |
self.win_width = 60 |
| 16 | 16 |
self.win_height = 13 |
| 17 | 17 |
|
| 18 |
- self.win_starty = (self.maxy - self.win_height) / 2 |
|
| 19 |
- self.win_startx = (self.maxx - self.win_width) / 2 |
|
| 18 |
+ self.win_starty = (self.maxy - self.win_height) // 2 |
|
| 19 |
+ self.win_startx = (self.maxx - self.win_width) // 2 |
|
| 20 | 20 |
|
| 21 | 21 |
self.menu_starty = self.win_starty + 6 |
| 22 | 22 |
|
| ... | ... |
@@ -28,7 +28,7 @@ class LinuxSelector(object): |
| 28 | 28 |
default_selected=0, tab_enable=False) |
| 29 | 29 |
|
| 30 | 30 |
self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, |
| 31 |
- 'Select Linux kernel to install', True, items=[], tab_enabled=False, |
|
| 31 |
+ 'Select Linux kernel to install', True, tab_enabled=False, |
|
| 32 | 32 |
position=1, can_go_next=True) |
| 33 | 33 |
self.window.set_action_panel(self.host_menu) |
| 34 | 34 |
|
| ... | ... |
@@ -126,7 +126,7 @@ if [ "$LIVE_CD" = false ] ; then |
| 126 | 126 |
cat >> ${BUILDROOT}/bin/bootphotoninstaller << EOF
|
| 127 | 127 |
#!/bin/bash |
| 128 | 128 |
cd /installer |
| 129 |
-[ \`tty\` == '/dev/tty1' ] && ./isoInstaller.py --json-file=$PACKAGE_LIST_FILE_BASE_NAME 2> /var/log/installer && shutdown -r now |
|
| 129 |
+[ \$(tty) == '/dev/tty1' ] && LANG=en_US.UTF-8 ./isoInstaller.py --json-file=$PACKAGE_LIST_FILE_BASE_NAME 2> /var/log/installer && shutdown -r now |
|
| 130 | 130 |
/bin/bash |
| 131 | 131 |
EOF |
| 132 | 132 |
|
| ... | ... |
@@ -24,12 +24,10 @@ default_partitions = [ |
| 24 | 24 |
{"mountpoint": "/", "size": 0, "filesystem": "ext4"},
|
| 25 | 25 |
] |
| 26 | 26 |
|
| 27 |
-def partition_compare(p1, p2): |
|
| 28 |
- if 'mountpoint' in p1 and 'mountpoint' in p2: |
|
| 29 |
- if len(p1['mountpoint']) == len(p2['mountpoint']): |
|
| 30 |
- return cmp(p1['mountpoint'], p2['mountpoint']) |
|
| 31 |
- return len(p1['mountpoint']) - len(p2['mountpoint']) |
|
| 32 |
- return 0 |
|
| 27 |
+def partition_compare(p): |
|
| 28 |
+ if 'mountpoint' in p: |
|
| 29 |
+ return (1, len(p['mountpoint']), p['mountpoint']) |
|
| 30 |
+ return (0, 0, "A") |
|
| 33 | 31 |
|
| 34 | 32 |
def partition_disk(disk, partitions): |
| 35 | 33 |
partitions_data = {}
|
| ... | ... |
@@ -128,7 +126,7 @@ def partition_disk(disk, partitions): |
| 128 | 128 |
partitions_data['boot_partition_number'] = partitions_data['root_partition_number'] |
| 129 | 129 |
partitions_data['bootdirectory'] = '/boot/' |
| 130 | 130 |
|
| 131 |
- partitions.sort(lambda p1,p2: partition_compare(p1, p2)) |
|
| 131 |
+ partitions.sort(key=lambda p: partition_compare(p)) |
|
| 132 | 132 |
|
| 133 | 133 |
return partitions_data |
| 134 | 134 |
|
| ... | ... |
@@ -14,7 +14,7 @@ def execute(name, config, root): |
| 14 | 14 |
script_file = os.path.join(root, 'etc/tmpfiles.d/postinstall.sh') |
| 15 | 15 |
|
| 16 | 16 |
with open(script_file, 'wb') as outfile: |
| 17 |
- outfile.write("\n".join(script))
|
|
| 17 |
+ outfile.write("\n".join(script).encode())
|
|
| 18 | 18 |
|
| 19 | 19 |
os.chmod(script_file, 0o700); |
| 20 | 20 |
with open(commons.KS_POST_INSTALL_LOG_FILE_NAME,"w") as logfile: |
| ... | ... |
@@ -12,6 +12,8 @@ def execute(name, config, root): |
| 12 | 12 |
hosts_file = os.path.join(root, 'etc/hosts') |
| 13 | 13 |
|
| 14 | 14 |
with open(hostname_file, 'wb') as outfile: |
| 15 |
- outfile.write(hostname) |
|
| 15 |
+ outfile.write(hostname.encode()) |
|
| 16 | 16 |
|
| 17 |
- commons.replace_string_in_file(hosts_file, r'127\.0\.0\.1\s+localhost\s*\Z', '127.0.0.1\tlocalhost\n127.0.0.1\t' + hostname) |
|
| 17 |
+ pattern = r'(127\.0\.0\.1)(\s+)(localhost)\s*\Z' |
|
| 18 |
+ replace = r'\1\2\3\n\1\2' + hostname |
|
| 19 |
+ commons.replace_string_in_file(hosts_file, pattern, replace) |
| ... | ... |
@@ -15,8 +15,8 @@ class PartitionISO(object): |
| 15 | 15 |
self.install_config = install_config |
| 16 | 16 |
self.path_checker = [] |
| 17 | 17 |
|
| 18 |
- self.win_starty = (self.maxy - self.win_height) / 2 |
|
| 19 |
- self.win_startx = (self.maxx - self.win_width) / 2 |
|
| 18 |
+ self.win_starty = (self.maxy - self.win_height) // 2 |
|
| 19 |
+ self.win_startx = (self.maxx - self.win_width) // 2 |
|
| 20 | 20 |
|
| 21 | 21 |
self.text_starty = self.win_starty + 4 |
| 22 | 22 |
self.text_height = self.win_height - 6 |
| ... | ... |
@@ -29,9 +29,9 @@ class PartitionISO(object): |
| 29 | 29 |
|
| 30 | 30 |
self.disk_size = [] |
| 31 | 31 |
for index, device in enumerate(self.devices): |
| 32 |
- self.disk_size.append((device.path, int(device.size) / 1048576)) |
|
| 32 |
+ self.disk_size.append((device.path, int(device.size) / 1048576)) |
|
| 33 | 33 |
|
| 34 |
- self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False, items=[], can_go_next=False) |
|
| 34 |
+ self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False, can_go_next=False) |
|
| 35 | 35 |
Device.refresh_devices() |
| 36 | 36 |
|
| 37 | 37 |
def display(self, params): |
| ... | ... |
@@ -60,7 +60,7 @@ class PartitionISO(object): |
| 60 | 60 |
self.table_space = 5 |
| 61 | 61 |
|
| 62 | 62 |
title = 'Current partitions:\n' |
| 63 |
- self.window.addstr(0, (self.win_width - len(title)) / 2, title) |
|
| 63 |
+ self.window.addstr(0, (self.win_width - len(title)) // 2, title) |
|
| 64 | 64 |
|
| 65 | 65 |
info = "Unpartitioned space: "+str(self.disk_size[self.device_index][1])+ " MB, Total size: "+ str(int(self.devices[self.device_index].size)/ 1048576)+" MB" |
| 66 | 66 |
|
| ... | ... |
@@ -163,7 +163,7 @@ class PartitionISO(object): |
| 163 | 163 |
if self.install_config['partitionsnumber'] == 0: |
| 164 | 164 |
window_height=9 |
| 165 | 165 |
window_width=40 |
| 166 |
- window_starty=(self.maxy-window_height)/2+5 |
|
| 166 |
+ window_starty=(self.maxy-window_height) // 2+5 |
|
| 167 | 167 |
confirm_window=ConfirmWindow(window_height,window_width,self.maxy, self.maxx, window_starty, 'Partition information cannot be empty', info=True) |
| 168 | 168 |
confirm_window.do_action() |
| 169 | 169 |
return self.display(False) |
| ... | ... |
@@ -171,7 +171,7 @@ class PartitionISO(object): |
| 171 | 171 |
if not self.has_slash: |
| 172 | 172 |
window_height=9 |
| 173 | 173 |
window_width=40 |
| 174 |
- window_starty=(self.maxy-window_height)/2+5 |
|
| 174 |
+ window_starty=(self.maxy-window_height) // 2 + 5 |
|
| 175 | 175 |
confirm_window=ConfirmWindow(window_height,window_width,self.maxy, self.maxx, window_starty, 'Missing /', info=True) |
| 176 | 176 |
confirm_window.do_action() |
| 177 | 177 |
return self.display(False) |
| ... | ... |
@@ -27,7 +27,7 @@ class ReadMulText(Action): |
| 27 | 27 |
self.default_string = default_string |
| 28 | 28 |
self.display_string = display_string |
| 29 | 29 |
self.textwin_width = maxx - self.horizontal_padding -2 |
| 30 |
- self.textwin_width = self.textwin_width*2/3 |
|
| 30 |
+ self.textwin_width = self.textwin_width* 2 // 3 |
|
| 31 | 31 |
self.visible_text_width = self.textwin_width - 1 |
| 32 | 32 |
self.position = 0 |
| 33 | 33 |
self.height = len(self.display_string) * 4 + 2 |
| ... | ... |
@@ -41,10 +41,10 @@ class ReadMulText(Action): |
| 41 | 41 |
self.shadowwin.bkgd(' ', curses.color_pair(0)) #Default shadow color
|
| 42 | 42 |
|
| 43 | 43 |
self.panel = curses.panel.new_panel(self.textwin) |
| 44 |
- self.panel.move((maxy-self.height)/2, (maxx - self.textwin_width) / 2 -1) |
|
| 44 |
+ self.panel.move((maxy-self.height) // 2, (maxx - self.textwin_width) // 2 -1) |
|
| 45 | 45 |
self.panel.hide() |
| 46 | 46 |
self.shadowpanel = curses.panel.new_panel(self.shadowwin) |
| 47 |
- self.shadowpanel.move((maxy-self.height)/2+1, (maxx - self.textwin_width) / 2) |
|
| 47 |
+ self.shadowpanel.move((maxy-self.height) // 2 + 1, (maxx - self.textwin_width) // 2) |
|
| 48 | 48 |
self.shadowpanel.hide() |
| 49 | 49 |
curses.panel.update_panels() |
| 50 | 50 |
|
| ... | ... |
@@ -87,7 +87,7 @@ class ReadText(Action): |
| 87 | 87 |
if self.str != self.install_config[self.field]: |
| 88 | 88 |
conf_message_height = 8 |
| 89 | 89 |
conf_message_width = 48 |
| 90 |
- conf_message_button_y = (self.maxy - conf_message_height) / 2 + 5 |
|
| 90 |
+ conf_message_button_y = (self.maxy - conf_message_height) // 2 + 5 |
|
| 91 | 91 |
confrim_window = ConfirmWindow(conf_message_height, conf_message_width, self.maxy, self.maxx, conf_message_button_y, self.confirmation_error_msg, True) |
| 92 | 92 |
confrim_window.do_action() |
| 93 | 93 |
return ActionResult(False, {'goBack': True})
|
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
# Copyright (C) 2015 vmware inc. |
| 2 | 2 |
# |
| 3 | 3 |
# Author: Mahmoud Bassiouny <mbassiouny@vmware.com> |
| 4 |
- |
|
| 5 | 4 |
import curses |
| 6 | 5 |
from actionresult import ActionResult |
| 7 | 6 |
from action import Action |
| ... | ... |
@@ -91,9 +90,13 @@ class TextPane(Action): |
| 91 | 91 |
|
| 92 | 92 |
|
| 93 | 93 |
def read_file(self, text_file_path, line_width): |
| 94 |
- with open(text_file_path, "r") as f: |
|
| 94 |
+ with open(text_file_path, "rb") as f: |
|
| 95 | 95 |
for line in f: |
| 96 | 96 |
# expand tab to 8 spaces. |
| 97 |
+ try: |
|
| 98 |
+ line = line.decode(encoding='latin1') |
|
| 99 |
+ except UnicodeDecodeError: |
|
| 100 |
+ pass |
|
| 97 | 101 |
line = line.expandtabs() |
| 98 | 102 |
indent = len(line) - len(line.lstrip()) |
| 99 | 103 |
actual_line_width = line_width - indent |
| ... | ... |
@@ -9,7 +9,7 @@ from action import Action |
| 9 | 9 |
|
| 10 | 10 |
class Window(Action): |
| 11 | 11 |
|
| 12 |
- def __init__(self, height, width, maxy, maxx, title, can_go_back, action_panel = None, items = [], menu_helper = None, position = 0, tab_enabled = True, can_go_next = False, read_text=False): |
|
| 12 |
+ def __init__(self, height, width, maxy, maxx, title, can_go_back, action_panel = None, items = None, menu_helper = None, position = 0, tab_enabled = True, can_go_next = False, read_text=False): |
|
| 13 | 13 |
self.can_go_back = can_go_back |
| 14 | 14 |
self.can_go_next = can_go_next |
| 15 | 15 |
self.height = height |
| ... | ... |
@@ -26,7 +26,10 @@ class Window(Action): |
| 26 | 26 |
self.read_text=read_text |
| 27 | 27 |
|
| 28 | 28 |
self.position = position |
| 29 |
- self.items = items |
|
| 29 |
+ if items: |
|
| 30 |
+ self.items = items |
|
| 31 |
+ else: |
|
| 32 |
+ self.items = [] |
|
| 30 | 33 |
self.menu_helper = menu_helper |
| 31 | 34 |
self.contentwin.addstr(0, (width - 1 - len(title)) // 2 , title)# |
| 32 | 35 |
newy = 5; |
| ... | ... |
@@ -38,7 +41,7 @@ class Window(Action): |
| 38 | 38 |
|
| 39 | 39 |
self.dist = 0 |
| 40 | 40 |
|
| 41 |
- if items: |
|
| 41 |
+ if len(self.items) > 0: |
|
| 42 | 42 |
#To select items, we need to identify up left right keys |
| 43 | 43 |
|
| 44 | 44 |
self.dist=self.width-11 |
| ... | ... |
@@ -19,12 +19,12 @@ class WindowStringReader(object): |
| 19 | 19 |
self.maxx = maxx |
| 20 | 20 |
self.maxy = maxy |
| 21 | 21 |
|
| 22 |
- self.startx = (self.maxx - self.width) / 2 |
|
| 23 |
- self.starty = (self.maxy - self.height) / 2 |
|
| 22 |
+ self.startx = (self.maxx - self.width) // 2 |
|
| 23 |
+ self.starty = (self.maxy - self.height) // 2 |
|
| 24 | 24 |
self.tab_enabled=False |
| 25 | 25 |
self.can_go_next=True |
| 26 | 26 |
|
| 27 |
- self.window = Window(self.height, self.width, self.maxy, self.maxx, self.title, True, items=[], tab_enabled=self.tab_enabled, position=1, can_go_next=self.can_go_next, read_text=self.can_go_next) |
|
| 27 |
+ self.window = Window(self.height, self.width, self.maxy, self.maxx, self.title, True, tab_enabled=self.tab_enabled, position=1, can_go_next=self.can_go_next, read_text=self.can_go_next) |
|
| 28 | 28 |
self.read_text = ReadText(maxy, maxx, self.window.content_window(), self.inputy, install_config, field, confirmation_err_msg, echo_char, accepted_chars, validation_fn, conversion_fn, default_string, tab_enabled=self.tab_enabled) |
| 29 | 29 |
self.window.set_action_panel(self.read_text) |
| 30 | 30 |
self.window.addstr(0, 0, self.display_string) |