Browse code

Fixing ostree installer screens

* Make the text window reader more generic to read any input.
* Add os tree server selection menu.

Mahmoud Bassiouny authored on 2015/08/14 11:00:47
Showing 6 changed files
... ...
@@ -14,6 +14,10 @@ import requests
14 14
 import json
15 15
 import time
16 16
 import os
17
+import cracklib
18
+import crypt
19
+import string
20
+import random
17 21
 from diskpartitioner import DiskPartitioner
18 22
 from packageselector import PackageSelector
19 23
 from custompackageselector import CustomPackageSelector
... ...
@@ -21,10 +25,11 @@ from installer import Installer
21 21
 from installercontainer import InstallerContainer
22 22
 from ostreeinstaller import OstreeInstaller
23 23
 from windowstringreader import WindowStringReader
24
+from ostreewindowstringreader import OSTreeWindowStringReader
24 25
 from jsonwrapper import JsonWrapper
25 26
 from selectdisk import SelectDisk
26 27
 from license import License
27
-import random
28
+from ostreeserverselector import OSTreeServerSelector
28 29
 
29 30
 class IsoInstaller(object):
30 31
 
... ...
@@ -75,7 +80,24 @@ class IsoInstaller(object):
75 75
             time.sleep(1)
76 76
         print "Failed to mount the cd, exiting the installer, check the logs for more details"
77 77
         raise Exception("Can not mount the cd")
78
-    
78
+
79
+    def validate_hostname(self, hostname):
80
+        error_msg = "It should start with alpha char and ends with alpha-numeric char"
81
+        if (hostname == None or len(hostname) == 0):
82
+            return False, error_msg
83
+        return (ord(hostname[0]) in self.alpha_chars) and (hostname[-1] not in ['.', '-']), error_msg
84
+
85
+    def validate_password(self, text):
86
+        try:
87
+            p = cracklib.VeryFascistCheck(text)
88
+        except ValueError, message:
89
+            p = str(message)
90
+        return p == text, "Error: " + p
91
+
92
+    def generate_password_hash(self,  password):
93
+        shadow_password = crypt.crypt(password, "$6$" + "".join([random.choice(string.ascii_letters + string.digits) for _ in range(16)]))
94
+        return shadow_password
95
+
79 96
     def __init__(self, stdscreen, options_file):
80 97
         self.screen = stdscreen
81 98
 
... ...
@@ -120,16 +142,64 @@ class IsoInstaller(object):
120 120
             license_agreement = License(self.maxy, self.maxx)
121 121
             select_disk = SelectDisk(self.maxy, self.maxx, install_config)
122 122
             package_selector = PackageSelector(self.maxy, self.maxx, install_config, options_file)
123
-            hostname_reader = WindowStringReader(self.maxy, self.maxx, 10, 70, 'hostname', False, 'Choose the hostname for your system',
124
-                'Hostname:', 
125
-                2, install_config,
126
-                random_hostname)
127
-            root_password_reader = WindowStringReader(self.maxy, self.maxx, 10, 70, 'password', False,  'Set up root password',
128
-                'Root password:', 
129
-                2, install_config)
130
-            confirm_password_reader = WindowStringReader(self.maxy, self.maxx, 10, 70, 'password', True,  'Confirm root password',
131
-                'Confirm Root password:', 
132
-                2, install_config)
123
+
124
+            self.alpha_chars = range(65, 91)
125
+            self.alpha_chars.extend(range(97,123))
126
+            hostname_accepted_chars = list(self.alpha_chars)
127
+            # Adding the numeric chars
128
+            hostname_accepted_chars.extend(range(48, 58))
129
+            # Adding the . and -
130
+            hostname_accepted_chars.extend([ord('.'), ord('-')])
131
+            
132
+            hostname_reader = WindowStringReader(
133
+                    self.maxy, self.maxx, 10, 70, 
134
+                    'hostname', 
135
+                    None, # confirmation error msg if it's a confirmation text
136
+                    None, # echo char
137
+                    hostname_accepted_chars, # set of accepted chars
138
+                    self.validate_hostname, # validation function of the input
139
+                    None, # post processing of the input field
140
+                    'Choose the hostname for your system', 'Hostname:', 2, install_config,
141
+                    random_hostname)
142
+            root_password_reader = WindowStringReader(
143
+                    self.maxy, self.maxx, 10, 70, 
144
+                    'password', 
145
+                    None, # confirmation error msg if it's a confirmation text
146
+                    '*', # echo char
147
+                    None, # set of accepted chars
148
+                    self.validate_password, # validation function of the input
149
+                    None,  # post processing of the input field
150
+                    'Set up root password', 'Root password:', 2, install_config)
151
+            confirm_password_reader = WindowStringReader(
152
+                    self.maxy, self.maxx, 10, 70, 
153
+                    'password', 
154
+                    "Passwords don't match, please try again.", # confirmation error msg if it's a confirmation text
155
+                    '*', # echo char
156
+                    None, # set of accepted chars
157
+                    None, # validation function of the input
158
+                    self.generate_password_hash, # post processing of the input field
159
+                    'Confirm root password', 'Confirm Root password:', 2, install_config)
160
+            ostree_server_selector = OSTreeServerSelector(self.maxy, self.maxx, install_config)
161
+            ostree_url_reader = OSTreeWindowStringReader(
162
+                    self.maxy, self.maxx, 10, 80, 
163
+                    'ostree_repo_url', 
164
+                    None, # confirmation error msg if it's a confirmation text
165
+                    None, # echo char
166
+                    None, # set of accepted chars
167
+                    None, # validation function of the input
168
+                    None, # post processing of the input field
169
+                    'Please provide the URL of OSTree repo', 'OSTree Repo URL:', 2, install_config,
170
+                    "https://dl.bintray.com/vmware/photon/rpm-ostree/dev/x86_64/minimal")
171
+            ostree_ref_reader = OSTreeWindowStringReader(
172
+                    self.maxy, self.maxx, 10, 70, 
173
+                    'ostree_repo_ref', 
174
+                    None, # confirmation error msg if it's a confirmation text
175
+                    None, # echo char
176
+                    None, # set of accepted chars
177
+                    None, # validation function of the input
178
+                    None, # post processing of the input field
179
+                    'Please provide the Ref in OSTree repo', 'OSTree Repo Ref:', 2, install_config,
180
+                    "dev/x86_64/minimal")
133 181
             
134 182
             items = items + [
135 183
                     (license_agreement.display, False),
... ...
@@ -138,6 +208,9 @@ class IsoInstaller(object):
138 138
                     (hostname_reader.get_user_string, True),
139 139
                     (root_password_reader.get_user_string, True),
140 140
                     (confirm_password_reader.get_user_string, False),
141
+                    (ostree_server_selector.display, True),
142
+                    (ostree_url_reader.get_user_string, True),
143
+                    (ostree_ref_reader.get_user_string, True),
141 144
                  ]
142 145
         else:
143 146
             install_config = ks_config
... ...
@@ -20,69 +20,17 @@ import urllib
20 20
 import modules.commons
21 21
 import xml.etree.ElementTree as ET
22 22
 from jsonwrapper import JsonWrapper
23
-from progressbar import ProgressBar
24
-from windowstringreader import WindowStringReader
25
-from window import Window
26
-from actionresult import ActionResult
27 23
 from installer import Installer
28
-from menu import Menu
29 24
 
30 25
 class OstreeInstaller(Installer):
31 26
 
32 27
     def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, rpm_path = "../stage/RPMS", log_path = "../stage/LOGS", ks_config = None):
33 28
         Installer.__init__(self, install_config, maxy, maxx, iso_installer, rpm_path, log_path, ks_config)
34
-        self.win_height = 13
35
-        self.win_width = 50
36
-        self.menu_starty = self.starty + 3    
37
-        self.ostree_host_menu_items = []
38
-        self.ostree_host_menu_items.append(("Default RPM-OSTree Server", self.default_installation, []))
39
-        self.ostree_host_menu_items.append(("Custom RPM-OSTree Server", self.custom_installation, []))
40
-        self.host_menu = Menu(self.menu_starty,  self.maxx, self.ostree_host_menu_items)
41
-        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select OSTree Server', True, self.host_menu)
42
-        self.default_repo = True
43
-
44
-    def default_installation(self,  selected_item_params):
45
-        self.default_repo = True
46
-        return ActionResult(True, None)
47
-
48
-    def custom_installation(self,  selected_item_params):
49
-        self.default_repo = False
50
-        success = False
51
-        while not success:
52
-            got_the_url = False
53
-            while not got_the_url:
54
-                ostree_url_reader = WindowStringReader(
55
-                    self.maxy, self.maxx, 10, 70,
56
-                    "ostree_repo_url", False,
57
-                    "Please provide the URL of OSTree repo",
58
-                    "OSTree Repo URL:", 2,
59
-                    self.install_config,
60
-                    "https://dl.bintray.com/vmware/photon/rpm-ostree/dev/x86_64/minimal")
61
-
62
-                ret = ostree_url_reader.get_user_string(None)
63
-                self.ostree_repo_url = ret.result
64
-                got_the_url = ret.success
65
-
66
-            ostree_ref_reader = WindowStringReader(self.maxy,
67
-                self.maxx, 10, 70,
68
-                "ostree_ref", False,
69
-                "Please provide the Ref in OSTree repo",
70
-                "OSTree Repo Ref:", 2,
71
-                self.install_config,
72
-                "dev/x86_64/minimal")
73
-
74
-            ret = ostree_ref_reader.get_user_string(None)
75
-            self.ostree_ref = ret.result
76
-            success = ret.success
77
-
78
-        return ActionResult(True, None)
79 29
 
80 30
     def get_ostree_repo_url(self):
81
-        if self.ks_config != None:
82
-            self.ostree_repo_url = self.ks_config['ostree_repo_url']
83
-            self.ostree_ref = self.ks_config['ostree_repo_ref']
84
-            return
85
-        self.window.do_action()
31
+        self.default_repo = 'default_repo' in self.install_config and self.install_config['default_repo'];
32
+        self.ostree_repo_url = self.install_config['ostree_repo_url']
33
+        self.ostree_ref = self.install_config['ostree_repo_ref']
86 34
 
87 35
     def deploy_ostree(self, repo_url, repo_ref):
88 36
         self.run("ostree admin --sysroot={} init-fs {}".format(self.photon_root, self.photon_root), "Initializing OSTree filesystem")
89 37
new file mode 100644
... ...
@@ -0,0 +1,42 @@
0
+#!/usr/bin/python2
1
+#
2
+#    Copyright (C) 2015 vmware inc.
3
+#
4
+#    Author: Mahmoud Bassiouny <mbassiouny@vmware.com>
5
+
6
+import json
7
+import os
8
+import curses
9
+from sets import Set
10
+from jsonwrapper import JsonWrapper
11
+from menu import Menu
12
+from window import Window
13
+from actionresult import ActionResult
14
+
15
+class OSTreeServerSelector(object):
16
+    def __init__(self,  maxy, maxx, install_config):
17
+        self.install_config = install_config
18
+        win_width = 50
19
+        win_height = 13
20
+
21
+        win_starty = (maxy - win_height) / 2
22
+        win_startx = (maxx - win_width) / 2
23
+
24
+        menu_starty = win_starty + 3
25
+
26
+        ostree_host_menu_items = [
27
+                                        ("Default RPM-OSTree Server", self.set_default_repo_installation, True),
28
+                                        ("Custom RPM-OSTree Server", self.set_default_repo_installation, False)
29
+                                    ]
30
+
31
+        host_menu = Menu(menu_starty,  maxx, ostree_host_menu_items)
32
+        self.window = Window(win_height, win_width, maxy, maxx, 'Select OSTree Server', True, host_menu)
33
+
34
+    def set_default_repo_installation(self,  is_default_repo ):
35
+        self.install_config['default_repo'] = is_default_repo
36
+        return ActionResult(True, None)
37
+
38
+    def display(self, params):
39
+        if self.install_config['type'] == 'ostree_host':
40
+            return self.window.do_action()
41
+        return ActionResult(True, None)
0 42
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+#!/usr/bin/python2
1
+#
2
+#    Copyright (C) 2015 vmware inc.
3
+#
4
+#    Author: Mahmoud Bassiouny <mbassiouny@vmware.com>
5
+
6
+from windowstringreader import WindowStringReader
7
+from actionresult import ActionResult
8
+
9
+class OSTreeWindowStringReader(WindowStringReader):
10
+    def __init__(self, maxy, maxx, height, width, field, confirmation_err_msg, echo_char, accepted_chars, validation_fn, conversion_fn, title, display_string, inputy, install_config, default_string = None):
11
+        WindowStringReader.__init__(self, maxy, maxx, height, width, field, confirmation_err_msg, echo_char, accepted_chars, validation_fn, conversion_fn, title, display_string, inputy, install_config, default_string)
12
+
13
+    def get_user_string(self, params):
14
+        if self.install_config['type'] == 'ostree_host' and not self.install_config['default_repo']:
15
+            return self.window.do_action()
16
+        return ActionResult(True, None)
... ...
@@ -5,24 +5,24 @@
5 5
 #    Author: Mahmoud Bassiouny <mbassiouny@vmware.com>
6 6
 
7 7
 import curses
8
-import crypt
9
-import string
10
-import random
11
-import cracklib
12 8
 import sys
13 9
 from actionresult import ActionResult
14 10
 from action import Action
15 11
 from confirmwindow import ConfirmWindow
16 12
 
17 13
 class ReadText(Action):
18
-    def __init__(self, maxy, maxx, textwin, y, install_config, field, confirm_pass, default_string = None):
14
+    def __init__(self, maxy, maxx, textwin, y, install_config, field, confirmation_error_msg, echo_char, accepted_chars, validation_fn, conversion_fn, default_string = None):
19 15
         self.textwin = textwin
20 16
         self.maxy = maxy
21 17
         self.maxx = maxx
22 18
         self.y = y
23 19
         self.install_config = install_config
24 20
         self.field = field
25
-        self.confirm_pass = confirm_pass;
21
+        self.confirmation_error_msg = confirmation_error_msg
22
+        self.echo_char = echo_char
23
+        self.accepted_chars = accepted_chars
24
+        self.validation_fn = validation_fn
25
+        self.conversion_fn = conversion_fn
26 26
         self.default_string = default_string
27 27
         self.textwin_width = self.textwin.getmaxyx()[1] - 1
28 28
         self.visible_text_width = self.textwin_width - 1
... ...
@@ -31,19 +31,8 @@ class ReadText(Action):
31 31
         self.maxlength = 255
32 32
 
33 33
         #initialize the accepted characters
34
-        if self.field == "password":
35
-            # Adding all the letters
36
-            self.accepted_chars = range(33, 127)
37
-        elif self.field == "hostname":
38
-
39
-            self.alpha_chars = range(65, 91)
40
-            self.alpha_chars.extend(range(97,123))
41
-            
42
-            self.accepted_chars = list(self.alpha_chars)
43
-            # Adding the numeric chars
44
-            self.accepted_chars.extend(range(48, 58))
45
-            # Adding the . and -
46
-            self.accepted_chars.extend([ord('.'), ord('-')])
34
+        if accepted_chars:
35
+            self.accepted_chars = accepted_chars
47 36
         else:
48 37
             self.accepted_chars = range(33, 127)
49 38
 
... ...
@@ -77,31 +66,20 @@ class ReadText(Action):
77 77
             ch = self.textwin.getch(self.y, curs_loc)
78 78
 
79 79
             if ch in [curses.KEY_ENTER, ord('\n')]:
80
-                if self.confirm_pass:
81
-                    if self.str != self.install_config['password']:
80
+                if self.confirmation_error_msg:
81
+                    if self.str != self.install_config[self.field]:
82 82
                         curses.curs_set(0)
83 83
                         conf_message_height = 8
84 84
                         conf_message_width = 48
85 85
                         conf_message_button_y = (self.maxy - conf_message_height) / 2 + 5
86
-                        confrim_window = ConfirmWindow(conf_message_height, conf_message_width, self.maxy, self.maxx, conf_message_button_y, "Passwords don't match, please try again.", True)
86
+                        confrim_window = ConfirmWindow(conf_message_height, conf_message_width, self.maxy, self.maxx, conf_message_button_y, self.confirmation_error_msg, True)
87 87
                         confrim_window.do_action()
88 88
                         return ActionResult(False, {'goBack': True})
89
-                    self.install_config['password'] = self.generate_password_hash(self.str)
90
-                elif self.field == "password":
91
-                    err = self.validate_password(self.str)
92
-                    if err != self.str:
93
-                        self.init_text()
94
-                        self.textwin.addstr(self.y + 2, 0, "Error: " + err, curses.color_pair(4))
95
-                        continue
96
-                    self.install_config['password'] = self.str;
97
-                elif self.field == "hostname":
98
-                    if not self.validate_hostname(self.str):
99
-                        self.textwin.addstr(self.y + 2, 0, "It should start with alpha char and ends with alpha-numeric char", curses.color_pair(4))
100
-                        continue
101
-                    self.install_config['hostname'] = self.str
89
+                    self.set_field()
102 90
                 else:
103
-                    self.install_config[self.field] = self.str
104
-                    return ActionResult(True, self.str)
91
+                    if not self.validate_input():
92
+                        continue
93
+                    self.set_field()
105 94
                 curses.curs_set(0)
106 95
                 return ActionResult(True, None)
107 96
             elif ch in [ord('\t')]:
... ...
@@ -122,24 +100,24 @@ class ReadText(Action):
122 122
                     text = self.str[-self.visible_text_width:]
123 123
                 else:
124 124
                     text = self.str
125
-                if self.field == "password":
126
-                    text = '*' * len(text)
125
+                if self.echo_char:
126
+                    text = self.echo_char * len(text)
127 127
                 # Add the dashes
128 128
                 text = text + '_' * (self.visible_text_width - len(self.str))
129 129
                 self.textwin.addstr(self.y, 0, text)
130 130
 
131
-    def validate_hostname(self, hostname):
132
-        if (hostname == None or len(hostname) == 0):
133
-            return False;
134
-        return (ord(hostname[0]) in self.alpha_chars) and (hostname[-1] not in ['.', '-'])
135
-
136
-    def validate_password(self, text):
137
-        try:
138
-            p = cracklib.VeryFascistCheck(text)
139
-        except ValueError, message:
140
-            p = str(message)
141
-        return p
142
-
143
-    def generate_password_hash(self,  password):
144
-        shadow_password = crypt.crypt(password, "$6$" + "".join([random.choice(string.ascii_letters + string.digits) for _ in range(16)]))
145
-        return shadow_password
131
+    def set_field(self):
132
+        if self.conversion_fn:
133
+            self.install_config[self.field] = self.conversion_fn(self.str)
134
+        else:
135
+            self.install_config[self.field] = self.str
136
+
137
+    def validate_input(self):
138
+        if self.validation_fn:
139
+            success, err = self.validation_fn(self.str)
140
+            if not success:
141
+                self.textwin.addstr(self.y + 2, 0, err, curses.color_pair(4))
142
+            return success
143
+        else:
144
+            return True
145
+
... ...
@@ -9,9 +9,10 @@ from window import Window
9 9
 from readtext import ReadText
10 10
 
11 11
 class WindowStringReader(object):
12
-    def __init__(self, maxy, maxx, height, width, field, confirm_password, title, display_string, inputy, install_config, default_string = None):
12
+    def __init__(self, maxy, maxx, height, width, field, confirmation_err_msg, echo_char, accepted_chars, validation_fn, conversion_fn, title, display_string, inputy, install_config, default_string = None):
13 13
         self.title = title
14 14
         self.display_string = display_string
15
+        self.install_config = install_config
15 16
         self.inputy = inputy
16 17
 
17 18
         self.width = width
... ...
@@ -23,7 +24,7 @@ class WindowStringReader(object):
23 23
         self.starty = (self.maxy - self.height) / 2
24 24
 
25 25
         self.window = Window(self.height, self.width, self.maxy, self.maxx, self.title, True)
26
-        self.read_text = ReadText(maxy, maxx, self.window.content_window(), self.inputy, install_config, field, confirm_password, default_string)
26
+        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)
27 27
         self.window.set_action_panel(self.read_text)
28 28
         self.window.addstr(0, 0, self.display_string)
29 29