7140893 Hide user account configuration in SCI tool if parent of home ZFS dataset is missing
7140902 In AI manifests, clarify linkage between shared ZFS datasets and sysconfig(1m)
--- a/usr/src/cmd/auto-install/manifest/ai_manifest.xml.src Mon Feb 27 13:40:50 2012 -0800
+++ b/usr/src/cmd/auto-install/manifest/ai_manifest.xml.src Wed Feb 29 08:45:58 2012 +0100
@@ -19,7 +19,7 @@
CDDL HEADER END
-Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
-->
<!--
===============================================================================
@@ -192,6 +192,24 @@
</disk>
<logical>
<zpool name="rpool" is_root="true">
+ <!--
+ Subsequent <filesystem> entries instruct an installer to create
+ following ZFS datasets:
+
+ <root_pool>/export (mounted on /export)
+ <root_pool>/export/home (mounted on /export/home)
+
+ Those datasets are part of standard environment and should be
+ always created.
+
+ In rare cases, if there is a need to deploy an installed system
+ without these datasets, either comment out or remove <filesystem>
+ entries. In such scenario, it has to be also assured that
+ in case of non-interactive post-install configuration, creation
+ of initial user account is disabled in related system
+ configuration profile. Otherwise the installed system would fail
+ to boot.
+ -->
<filesystem name="export" mountpoint="/export"/>
<filesystem name="export/home"/>
<be name="solaris"/>
--- a/usr/src/cmd/auto-install/manifest/default.xml.src Mon Feb 27 13:40:50 2012 -0800
+++ b/usr/src/cmd/auto-install/manifest/default.xml.src Wed Feb 29 08:45:58 2012 +0100
@@ -19,7 +19,7 @@
CDDL HEADER END
- Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
-->
<!DOCTYPE auto_install SYSTEM "file:///usr/share/install/ai.dtd.@DTD_VERSION_AI@">
@@ -28,6 +28,24 @@
<target>
<logical>
<zpool name="rpool" is_root="true">
+ <!--
+ Subsequent <filesystem> entries instruct an installer to create
+ following ZFS datasets:
+
+ <root_pool>/export (mounted on /export)
+ <root_pool>/export/home (mounted on /export/home)
+
+ Those datasets are part of standard environment and should be
+ always created.
+
+ In rare cases, if there is a need to deploy an installed system
+ without these datasets, either comment out or remove <filesystem>
+ entries. In such scenario, it has to be also assured that
+ in case of non-interactive post-install configuration, creation
+ of initial user account is disabled in related system
+ configuration profile. Otherwise the installed system would fail
+ to boot.
+ -->
<filesystem name="export" mountpoint="/export"/>
<filesystem name="export/home"/>
<be name="solaris"/>
--- a/usr/src/cmd/auto-install/manifest/zone_default.xml.src Mon Feb 27 13:40:50 2012 -0800
+++ b/usr/src/cmd/auto-install/manifest/zone_default.xml.src Wed Feb 29 08:45:58 2012 +0100
@@ -19,7 +19,7 @@
CDDL HEADER END
- Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
-->
<!DOCTYPE auto_install SYSTEM "file:///usr/share/install/ai.dtd.@DTD_VERSION_AI@">
@@ -29,6 +29,24 @@
<target>
<logical>
<zpool name="rpool">
+ <!--
+ Subsequent <filesystem> entries instruct an installer
+ to create following ZFS datasets:
+
+ <root_pool>/export (mounted on /export)
+ <root_pool>/export/home (mounted on /export/home)
+
+ Those datasets are part of standard environment
+ and should be always created.
+
+ In rare cases, if there is a need to deploy a zone
+ without these datasets, either comment out or remove
+ <filesystem> entries. In such scenario, it has to be also
+ assured that in case of non-interactive post-install
+ configuration, creation of initial user account is
+ disabled in related system configuration profile.
+ Otherwise the installed zone would fail to boot.
+ -->
<filesystem name="export" mountpoint="/export"/>
<filesystem name="export/home"/>
<be name="solaris">
--- a/usr/src/cmd/system-config/__init__.py Mon Feb 27 13:40:50 2012 -0800
+++ b/usr/src/cmd/system-config/__init__.py Wed Feb 29 08:45:58 2012 +0100
@@ -234,7 +234,10 @@
result.append(TimeZone(main_win, screen=TimeZone.LOCATIONS))
result.append(TimeZone(main_win))
result.append(DateTimeScreen(main_win))
- result.append(UserScreen(main_win))
+
+ # All screens are to be brought up, so make sure complete user screen
+ # (root password as well as initial user account) is displayed.
+ result.append(UserScreen(main_win, show_user_account=True))
return result
--- a/usr/src/cmd/system-config/helpfiles/users.txt Mon Feb 27 13:40:50 2012 -0800
+++ b/usr/src/cmd/system-config/helpfiles/users.txt Wed Feb 29 08:45:58 2012 +0100
@@ -2,6 +2,8 @@
In this screen, you can type a root password and define a user account for your installed system.
+Note: When a user account is added, the home directory is created on the <root_pool>/export/home/<login> ZFS dataset. The parent of that dataset, <root_pool>/export/home, must exist in order for you to be allowed to create the user account.
+
GUIDELINE
* Both the root password and user account are optional. But, completing all fields in the screen is recommended.
--- a/usr/src/cmd/system-config/test/test_users.py Mon Feb 27 13:40:50 2012 -0800
+++ b/usr/src/cmd/system-config/test/test_users.py Wed Feb 29 08:45:58 2012 +0100
@@ -19,7 +19,7 @@
#
# CDDL HEADER END
#
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
#
'''
@@ -101,6 +101,7 @@
self.UserScreen__init__ = UserScreen.__init__
UserScreen.__init__ = lambda x, y: None
self.user_screen = UserScreen(None)
+ self.user_screen.show_user_account = True
def tearDown(self):
UserScreen.__init__ = self.UserScreen__init__
--- a/usr/src/cmd/system-config/users.py Mon Feb 27 13:40:50 2012 -0800
+++ b/usr/src/cmd/system-config/users.py Wed Feb 29 08:45:58 2012 +0100
@@ -19,7 +19,7 @@
#
# CDDL HEADER END
#
-# Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
#
'''
@@ -28,6 +28,7 @@
import logging
+from solaris_install import CalledProcessError, run
from solaris_install.logger import INSTALL_LOGGER_NAME
from solaris_install.sysconfig import _, SCI_HELP
import solaris_install.sysconfig.profile
@@ -44,6 +45,9 @@
LOGGER = None
+# Consumed CLIs
+ZFS = "/usr/sbin/zfs"
+
class UserScreen(BaseScreen):
'''Allow user to set:
@@ -55,8 +59,11 @@
'''
HEADER_TEXT = _("Users")
- INTRO = _("Define a root password for the system and user account for"
- " yourself.")
+ INTRO_SHOW_USER = _("Define a root password for the system and user "
+ "account for yourself.")
+ INTRO_HIDE_USER = _("Define a root password for the system. Creation "
+ "of user account is disabled. See Help for "
+ "additional information.")
ROOT_TEXT = _("System Root Password")
ROOT_LABEL = _("Root password:")
CONFIRM_LABEL = _("Confirm password:")
@@ -70,7 +77,7 @@
PASS_SCREEN_LEN = 16 # also used as column width for user input
ITEM_OFFSET = 2
- def __init__(self, main_win):
+ def __init__(self, main_win, show_user_account=None):
global LOGGER
if LOGGER is None:
LOGGER = logging.getLogger(INSTALL_LOGGER_NAME + ".sysconfig")
@@ -116,7 +123,27 @@
self.user_confirm_err = None
self.user_confirm_list = None
self.user_confirm_edit = None
-
+
+ #
+ # If show_user_account is True, complete Users screen will be
+ # displayed (root password edit fields as well as widgets related
+ # to creating initial user account). Otherwise, widgets for initial
+ # user account are hidden.
+ #
+ # If not initialized explicitly by caller (e.g. text installer),
+ # show_user_account is set to True only if all conditions needed
+ # for successful creation of user account are met.
+ #
+ # In particular, existence of parent of home ZFS dataset is checked,
+ # since if parent of home ZFS dataset was missing, then
+ # svc:/system/config-user smf service (responsible for configuring
+ # root and user accounts) would fail to create user account. As
+ # a result of that, the whole system would end up in maintenance mode.
+ #
+ self.show_user_account = show_user_account
+ if self.show_user_account is None:
+ self.show_user_account = self.home_zfs_parent_exists()
+
def _show(self):
'''Display the user name, real name, and password fields'''
@@ -128,10 +155,10 @@
#
if sc_profile.users is None:
#
- # assume root is a role. root is later changed to normal account
- # if user account is not created
+ # Assume root is a normal account. It will be changed to a role
+ # if the user account is created.
#
- root = UserInfo(login_name="root", is_role=True)
+ root = UserInfo(login_name="root", is_role=False)
#
# Initialize attributes of user account which can't be configured
@@ -153,8 +180,13 @@
y_loc = 1
- self.center_win.add_paragraph(UserScreen.INTRO, y_loc, 1, y_loc + 2,
- self.win_size_x - 1)
+ if self.show_user_account:
+ intro_text = UserScreen.INTRO_SHOW_USER
+ else:
+ intro_text = UserScreen.INTRO_HIDE_USER
+
+ self.center_win.add_paragraph(intro_text, y_loc, 1,
+ y_loc + 2, self.win_size_x - 1)
y_loc += 3
self.center_win.add_text(UserScreen.ROOT_TEXT, y_loc, 1,
@@ -187,7 +219,13 @@
error_win=self.root_confirm_err)
rc_edit_kwargs = {"linked_win": self.root_pass_edit}
self.root_confirm_edit.on_exit_kwargs = rc_edit_kwargs
-
+
+ # User account disabled, do not display related widgets.
+ if not self.show_user_account:
+ self.main_win.do_update()
+ self.center_win.activate_object(self.root_pass_list)
+ return
+
y_loc += 2
self.center_win.add_text(UserScreen.USER_TEXT, y_loc, 1,
self.win_size_x - 1)
@@ -251,11 +289,8 @@
self.center_win.activate_object(self.root_pass_list)
def on_change_screen(self):
- '''Save real name and login name always'''
- self.user.real_name = self.real_name_edit.get_text()
- self.user.login_name = self.username_edit.get_text()
- self.root.is_role = bool(self.user.login_name)
-
+ '''Store entered information into data object cache.'''
+
if self.root_pass_edit.modified:
if self.root_pass_edit.compare(self.root_confirm_edit):
self.root.password = self.root_pass_edit.get_text()
@@ -267,6 +302,19 @@
self.root_pass_edit.clear_text()
self.root_confirm_edit.clear_text()
+
+ #
+ # User account disabled, do not store related info into data object
+ # cache.
+ #
+ if not self.show_user_account:
+ return
+
+ self.user.real_name = self.real_name_edit.get_text()
+ self.user.login_name = self.username_edit.get_text()
+
+ # If user account is to be created, configure root account as a role.
+ self.root.is_role = bool(self.user.login_name)
if self.user_pass_edit.modified:
if self.user_pass_edit.compare(self.user_confirm_edit):
@@ -282,26 +330,16 @@
def validate(self):
'''Check for mismatched passwords, bad login names, etc.'''
- if self.user_pass_edit.modified:
- user_pass_set = bool(self.user_pass_edit.get_text())
- else:
- user_pass_set = (self.user.passlen != 0)
-
- if self.root_pass_edit.modified:
- root_pass_set = bool(self.root_pass_edit.get_text())
- else:
- root_pass_set = (self.root.passlen != 0)
-
- login_name = self.username_edit.get_text()
- LOGGER.debug("login_name=%s", login_name)
- real_name = self.real_name_edit.get_text()
- LOGGER.debug("real_name=%s", real_name)
-
# Note: the Root and User password fields may be filled with
# asterisks if the user previously invoked this screen. Therefore,
# if not empty we check their modified flags before validating the
# contents.
+ if self.root_pass_edit.modified:
+ root_pass_set = bool(self.root_pass_edit.get_text())
+ else:
+ root_pass_set = (self.root.passlen != 0)
+
# Root password is mandatory and, if modified, must be valid
if not root_pass_set or self.root_pass_edit.modified:
pass_valid(self.root_pass_edit,
@@ -310,6 +348,20 @@
if not self.root_pass_edit.compare(self.root_confirm_edit):
raise UIMessage(_("Root passwords don't match"))
+ # User account disabled, skip related validation steps.
+ if not self.show_user_account:
+ return
+
+ if self.user_pass_edit.modified:
+ user_pass_set = bool(self.user_pass_edit.get_text())
+ else:
+ user_pass_set = (self.user.passlen != 0)
+
+ login_name = self.username_edit.get_text()
+ LOGGER.debug("login_name=%s", login_name)
+ real_name = self.real_name_edit.get_text()
+ LOGGER.debug("real_name=%s", real_name)
+
# Username (if specified) must be valid
login_valid(self.username_edit)
@@ -329,6 +381,35 @@
raise UIMessage(_("Enter username or clear all user "
"account fields"))
+ def home_zfs_parent_exists(self):
+ '''Return True if parent of to-be-created home ZFS dataset exists.
+ Otherwise, return False.
+
+ Home ZFS dataset is created in form of <root_pool>/export/home/<login>
+ with mountpoint /export/home/<login> inherited from parent dataset.
+
+ The check verifies that ZFS dataset with to-be-inherited /export/home
+ mountpoint exists.
+ '''
+
+ # obtain mountpoints for all existing ZFS datasets
+ cmd = [ZFS, "list", "-H", "-o", "mountpoint"]
+ try:
+ mountpoint_list = run(cmd, logger=LOGGER)
+ except CalledProcessError:
+ LOGGER.warn("Could not determine if parent of home ZFS dataset "
+ "exists.")
+ return False
+
+ if "/export/home" not in mountpoint_list.stdout.splitlines():
+ LOGGER.debug("Parent of home ZFS dataset does not exist, creation "
+ "of user account disabled.")
+ return False
+
+ LOGGER.debug("Parent of home ZFS dataset exists, creation of user "
+ "account enabled.")
+ return True
+
def username_valid(edit_field):
'''Validate username'''