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) |