Browse code

Python3 isoinstaller.

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>

xiaolin-vmware authored on 2017/11/17 10:17:22
Showing 18 changed files
... ...
@@ -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)