7162079 CPIO installation propagates install root password to the target
7098714 system/network installed via GUI and text installer fails pkg verify check
7093060 pre_pkg_img_mod needs a streamlined method for saving files for later restoration by ICTs
--- a/usr/src/cmd/distro_const/checkpoints/pre_pkg_img_mod.py Mon May 07 17:32:38 2012 -0700
+++ b/usr/src/cmd/distro_const/checkpoints/pre_pkg_img_mod.py Wed May 09 08:51:52 2012 +0200
@@ -111,9 +111,84 @@
for profile in svc_profile_list:
self.svc_profiles.append(profile.source)
- def set_password(self):
- """ class method to set the root password
+ def save_files_directories(self, save_list, preserve=False):
+ """ class method for saving key files and directories for restoration
+ after installation. File permissions and ownership are preserved.
+ Missing target directories are created.
+
+ If 'preserve' is set to False (default case), files are moved
+ to save area (source files are removed).
+ If 'preserve' is set to True, files are copied to save area
+ (source files are preserved).
"""
+
+ for df in save_list:
+ # If object does not exist, skip it.
+ full_path = os.path.join(self.pkg_img_path, df)
+ dest_path = os.path.join(self.save_path, df)
+ if not os.path.exists(full_path):
+ self.logger.error("WARNING: unable to find " + full_path +
+ " to save for later restoration!")
+ continue
+
+ # If object is directory, just create it in save area.
+ if os.path.isdir(full_path) and not os.path.exists(dest_path):
+ os.makedirs(dest_path)
+ continue
+
+ # If object is file, copy (or move it) to save area. Create missing
+ # directories as part of that process.
+ if os.path.isfile(full_path) and not os.path.exists(dest_path):
+ dir_path = os.path.dirname(dest_path)
+ if not os.path.exists(dir_path):
+ os.makedirs(dir_path)
+
+ if preserve:
+ # copy the file along with its metadata
+ shutil.copy2(full_path, dir_path)
+
+ # shutil.copy2() does not preserve file ownership,
+ # take care of that manually.
+ df_stat = os.lstat(full_path)
+ os.lchown(dest_path, df_stat.st_uid, df_stat.st_gid)
+ else:
+ # move the file and preserve file metadata
+ shutil.move(full_path, dir_path)
+
+ def customize_config_files(self):
+ """ class method to save and/or tune various configuration files
+ """
+
+ #
+ # Save the original /boot/grub/menu.lst file if it exists. It will not
+ # exist on GRUB2 based images so it is silently ignored if not present.
+ # Specific to i386 platform.
+ #
+ if platform.processor() == "i386":
+ save_list = ["boot/grub/menu.lst"]
+ if os.path.exists(os.path.join(self.pkg_img_path, save_list[0])):
+ self.save_files_directories(save_list)
+
+ #
+ # Save pristine /etc/inet/hosts file, since it is modified by
+ # identity:node smf service during media boot.
+ #
+ self.save_files_directories(["etc/inet/hosts"], preserve=True)
+
+ #
+ # Modify /etc/system to make ZFS consume less memory.
+ # Save original system(4) file.
+ #
+ self.save_files_directories(["etc/system"], preserve=True)
+
+ etc_system = os.path.join(self.pkg_img_path, "etc/system")
+ with open(etc_system, "a+") as fh:
+ fh.write("set zfs:zfs_arc_max=0x4002000\n")
+ fh.write("set zfs:zfs_vdev_cache_size=0\n")
+
+ # Configure the root password. Save original shadow(4) file.
+ self.save_files_directories(["etc/shadow"], preserve=True)
+
self.logger.debug("root password is: " + self.root_password)
self.logger.debug("is root password plaintext: " +
str(self.is_plaintext))
@@ -132,130 +207,40 @@
pfile.setvalue(root_entry)
pfile.writefile()
- def modify_etc_system(self):
- """ class method to modify etc/system
- """
- # path to the save directory
- save_path = os.path.join(self.pkg_img_path, "save")
-
- if not os.path.exists(save_path):
- os.mkdir(save_path)
-
- # create /save/etc directory, if needed
- if not os.path.exists(os.path.join(save_path, "etc")):
- os.mkdir(os.path.join(save_path, "etc"))
-
- # save a copy of /etc/system
- etc_system = os.path.join(self.pkg_img_path, "etc/system")
- if os.path.exists(etc_system):
- shutil.copy2(etc_system, os.path.join(save_path, "etc/system"))
-
- # Modify /etc/system to make ZFS consume less memory
- with open(etc_system, "a+") as fh:
- fh.write("set zfs:zfs_arc_max=0x4002000\n")
- fh.write("set zfs:zfs_vdev_cache_size=0\n")
-
- def modify_dhcpagent(self):
- """ method to modify /etc/default/dhcpagent to include the Rootpath
- bootp-dhcp-parameter for finding iSCSI information from the DHCP server
-
- This method can be removed if/when CR 7129888 is addressed
- """
+ #
+ # Modify /etc/default/dhcpagent to include the Rootpath
+ # bootp-dhcp-parameter for finding iSCSI information from the DHCP
+ # server.
+ #
+ # This method can be removed if/when CR 7129888 is addressed.
+ #
# verify /etc/default/dhcpagent exists
dhcpagent = os.path.join(self.pkg_img_path, "etc/default/dhcpagent")
if not os.path.exists(dhcpagent):
- self.logger.debug("skipping save of /etc/default/dhcpagent")
- return
-
- # path to the save directory
- save_path = os.path.join(self.pkg_img_path, "save")
-
- if not os.path.exists(save_path):
- os.mkdir(save_path)
-
- # create /save/etc/default directory, if needed
- if not os.path.exists(os.path.join(save_path, "etc/default")):
- os.makedirs(os.path.join(save_path, "etc/default"))
-
- # save a copy of /etc/default/dhcpagent
- shutil.copy2(dhcpagent,
- os.path.join(save_path, "etc/default/dhcpagent"))
-
- # open the file and read it into memory
- with open(dhcpagent, "r") as fh:
- contents = fh.read()
+ self.logger.debug("skipping modification of "
+ "/etc/default/dhcpagent, file does not exist")
+ else:
+ # Save pristine dhcpagent file.
+ self.save_files_directories(["etc/default/dhcpagent"],
+ preserve=True)
- new_file = list()
- for line in contents.splitlines():
- if line.startswith("PARAM_REQUEST_LIST") and "17" not in line:
- # append the parameter to enable rootpath
- new_file.append(line + ",17")
- else:
- new_file.append(line)
-
- # rewrite the file
- with open(dhcpagent, "w+") as fh:
- fh.write("\n".join(new_file))
- fh.write("\n")
-
- def save_etc_inet_hosts(self):
- """ class method to save pristine hosts(4) file. hosts(4) file
- is modified by identity:node smf service and we don't want those
- changes to be propagated to the target system.
- """
- # create save/etc/inet directory, if needed
- if not os.path.exists(os.path.join(self.save_path, "etc/inet")):
- os.makedirs(os.path.join(self.save_path, "etc/inet"))
+ # open the file and read it into memory
+ with open(dhcpagent, "r") as fh:
+ contents = fh.read()
- # save a copy of /etc/inet/hosts
- etc_inet_hosts = os.path.join(self.pkg_img_path, "etc/inet/hosts")
- if os.path.exists(etc_inet_hosts):
- shutil.copy2(etc_inet_hosts, os.path.join(self.save_path,
- "etc/inet/hosts"))
-
- def save_menu_lst(self):
- """ class method to save the original /boot/grub/menu.lst file if it
- exists. It will not exist on GRUB2 based images so it is silently
- ignored if not present.
- """
-
- save_list = ["boot/grub/menu.lst"]
- if os.path.exists(os.path.join(self.pkg_img_path, save_list[0])):
- self.save_files_directories(save_list)
-
- def save_files_directories(self, save_list=None):
- """ class method for saving key files and directories for restoration
- after installation. Missing target directories are created.
- """
+ new_file = list()
+ for line in contents.splitlines():
+ if line.startswith("PARAM_REQUEST_LIST") and "17" not in line:
+ # append the parameter to enable rootpath
+ new_file.append(line + ",17")
+ else:
+ new_file.append(line)
- # If there is nothing to save, just return
- if save_list is None or len(save_list) == 0:
- return
-
- for df in save_list:
- # If object does not exist, skip it.
- full_path = os.path.join(self.pkg_img_path, df)
- dest_path = os.path.join(self.save_path, df)
- if not os.path.exists(full_path):
- self.logger.error("WARNING: unable to find " + full_path +
- " to save for later restoration!")
- continue
-
- # If object is directory, just create it in save area.
- if os.path.isdir(full_path) and not os.path.exists(dest_path):
- os.makedirs(dest_path)
- continue
-
- # If object is file, move it to save area. Create missing
- # directories as part of that process.
- if os.path.isfile(full_path) and not os.path.exists(dest_path):
- dir_path = os.path.dirname(dest_path)
- if not os.path.exists(dir_path):
- os.makedirs(dir_path)
-
- # move the file and preserve file metadata
- shutil.move(full_path, dir_path)
+ # rewrite the file
+ with open(dhcpagent, "w+") as fh:
+ fh.write("\n".join(new_file))
+ fh.write("\n")
def configure_smf(self):
""" class method for the configuration of SMF manifests
@@ -362,11 +347,8 @@
self.parse_doc()
- # set root's password
- self.set_password()
-
- # save /boot/grub/menu.lst
- self.save_menu_lst()
+ # Customize various configuration files.
+ self.customize_config_files()
# preload smf manifests
self.configure_smf()
@@ -490,11 +472,8 @@
self.parse_doc()
- # set root's password
- self.set_password()
-
- # save /boot/grub/menu.lst
- self.save_menu_lst()
+ # Customize various configuration files.
+ self.customize_config_files()
# preload smf manifests
self.configure_smf()
@@ -532,10 +511,6 @@
self.get_pkg_version("auto-install")
self.get_license()
- self.modify_etc_system()
-
- # modify /etc/default/dhcpagent
- self.modify_dhcpagent()
# write out the .image_info file
self.calculate_size()
@@ -575,17 +550,20 @@
"""
return 180
- def save_files(self):
- """ class method for saving key files and directories for restoration
- after installation. Files are moved to the 'save' area.
+ def customize_config_files(self):
+ """ class method for saving and/or modifying key files. Files are
+ saved for restoration after installation.
+
+ In addition to standard set of key files, LiveCD also tunes
+ GNOME configuration.
"""
- self.logger.debug("Creating the save directory with files and " +
- "directories for restoration after installation")
+
+ # First carry out customization common to all media type.
+ super(LiveCDPrePkgImgMod, self).customize_config_files()
# remove gnome-power-manager, vp-sysmon, and updatemanagernotifier
# from the liveCD and restore after installation
save_list = [
- "etc/gconf/schemas",
"etc/xdg/autostart/updatemanagernotifier.desktop",
"usr/share/dbus-1/services/gnome-power-manager.service",
"usr/share/gnome/autostart/gnome-power-manager.desktop",
@@ -599,10 +577,10 @@
# installation
panel_file = "etc/gconf/schemas/panel-default-setup.entries"
- # copy the file to the save directory first
- shutil.copy2(os.path.join(self.pkg_img_path, panel_file),
- os.path.join(self.save_path, panel_file))
+ # save the file first
+ self.save_files_directories([panel_file], preserve=True)
+ # now modify it
with open(os.path.join(self.pkg_img_path, panel_file), "r") as fh:
panel_file_data = fh.read()
@@ -730,27 +708,12 @@
self.parse_doc()
- # set root's password
- self.set_password()
-
- # save /boot/grub/menu.lst
- self.save_menu_lst()
+ # Customize various configuration files.
+ self.customize_config_files()
# preload smf manifests
self.configure_smf()
- # save key files and directories
- self.save_files()
-
- # modify /etc/system
- self.modify_etc_system()
-
- # modify /etc/default/dhcpagent
- self.modify_dhcpagent()
-
- # save pristine /etc/inet/hosts file
- self.save_etc_inet_hosts()
-
# create the gnome caches
self.generate_gnome_caches()
@@ -784,21 +747,12 @@
self.parse_doc()
- # set root's password
- self.set_password()
+ # Customize various configuration files.
+ self.customize_config_files()
# preload smf manifests
self.configure_smf()
- # modify /etc/system
- self.modify_etc_system()
-
- # modify /etc/default/dhcpagent
- self.modify_dhcpagent()
-
- # save pristine /etc/inet/hosts file
- self.save_etc_inet_hosts()
-
# write out the .image_info file
self.calculate_size()
--- a/usr/src/cmd/distro_const/checkpoints/test/test_pre_pkg_img_mod.py Mon May 07 17:32:38 2012 -0700
+++ b/usr/src/cmd/distro_const/checkpoints/test/test_pre_pkg_img_mod.py Wed May 09 08:51:52 2012 +0200
@@ -57,17 +57,20 @@
</service>"""
-class TestSetPassword(unittest.TestCase):
- """ test case to test setting the password
+class TestCustomizeConfigFiles(unittest.TestCase):
+ """ test case to test customizing various configuration files
"""
def setUp(self):
# create a dummy filesystem with some files created in the proper
# location
engine_test_utils.get_new_engine_instance()
- self.pi_filelist = ["/etc/passwd", "/etc/shadow", "/usr/lib/"]
+ self.pi_filelist = ["etc/passwd", "etc/shadow", "etc/system",
+ "etc/default/dhcpagent", "etc/inet/hosts",
+ "usr/lib/"]
self.ppim = PrePkgImgMod("Test PPIM")
self.ppim.pkg_img_path = testlib.create_filesystem(*self.pi_filelist)
+ self.ppim.save_path = os.path.join(self.ppim.pkg_img_path, "save")
# copy /usr/lib/libc.so and /usr/lib/libc.so.1 to the pkg_img_path
shutil.copy2("/usr/lib/libc.so",
@@ -82,55 +85,30 @@
shutil.copy2("/etc/shadow", os.path.join(self.ppim.pkg_img_path,
"etc/shadow"))
+ # write a simplified line to the dhcpagent file
+ with open(os.path.join(self.ppim.pkg_img_path,
+ "etc/default/dhcpagent"),
+ "w") as fh:
+ fh.write("PARAM_REQUEST_LIST=1,3,6,12,15,28,43\n")
+
def tearDown(self):
shutil.rmtree(self.ppim.pkg_img_path, ignore_errors=True)
engine_test_utils.reset_engine()
- def test_cleartext_password(self):
- self.ppim.root_password = "test"
- self.ppim.is_plaintext = "true"
- self.ppim.set_password()
-
- with open(os.path.join(self.ppim.pkg_img_path, "etc/shadow")) as fh:
- line = fh.readline()
- if line.startswith("root"):
- self.assert_(self.ppim.root_password not in line)
-
- def test_encrypted_password(self):
+ def test_customize_config_files(self):
self.ppim.root_password = "test"
self.ppim.is_plaintext = "false"
- self.ppim.set_password()
+
+ self.ppim.customize_config_files()
+ # /etc/shadow
+ # verify that root password was set up
with open(os.path.join(self.ppim.pkg_img_path, "etc/shadow")) as fh:
line = fh.readline()
if line.startswith("root"):
self.assert_(self.ppim.root_password in line)
-
-class TestEtcSystemModification(unittest.TestCase):
- """ test case for testing the modification of /etc/system
- """
-
- def setUp(self):
- # create a dummy filesystem with some files created in the proper
- # location
- engine_test_utils.get_new_engine_instance()
- self.ppim = PrePkgImgMod("Test PPIM")
- self.pi_filelist = ["/etc/system"]
- self.ppim.pkg_img_path = testlib.create_filesystem(*self.pi_filelist)
-
- def tearDown(self):
- shutil.rmtree(self.ppim.pkg_img_path, ignore_errors=True)
- engine_test_utils.reset_engine()
-
- def test_modify_etc_system(self):
- # test with a missing /etc/system file
- self.ppim.modify_etc_system()
-
- # verify the save directory entry exists
- self.assert_(os.path.isdir(os.path.join(self.ppim.pkg_img_path,
- "save/etc")))
-
+ # /etc/system
# verify the 'saved' /etc/system file is 0 bytes
self.assert_(os.path.getsize(os.path.join(self.ppim.pkg_img_path,
"save/etc/system")) == 0)
@@ -142,32 +120,7 @@
"etc/system")]
self.assertEqual(run_silent(cmd).wait(), 0)
-
-class TestDhcpagentModification(unittest.TestCase):
- """ test case for testing the modification of /etc/default/dhcpagent
- """
-
- def setUp(self):
- # create a dummy filesystem with some files created in the proper
- # location
- engine_test_utils.get_new_engine_instance()
- self.ppim = PrePkgImgMod("Test PPIM")
- self.pi_filelist = ["/etc/default/dhcpagent"]
- self.ppim.pkg_img_path = testlib.create_filesystem(*self.pi_filelist)
-
- def tearDown(self):
- shutil.rmtree(self.ppim.pkg_img_path, ignore_errors=True)
- engine_test_utils.reset_engine()
-
- def test_modify_dhcpagent(self):
- # write a simplified line to the dhcpagent file
- with open(os.path.join(self.ppim.pkg_img_path,
- "etc/default/dhcpagent"),
- "w") as fh:
- fh.write("PARAM_REQUEST_LIST=1,3,6,12,15,28,43\n")
-
- self.ppim.modify_dhcpagent()
-
+ # /etc/default/dhcpagent
# verify the save directory entry exists
self.assert_(os.path.isdir(os.path.join(self.ppim.pkg_img_path,
"save/etc/default")))
@@ -178,6 +131,10 @@
p = run(cmd)
self.assertTrue("17" in p.stdout)
+ # verify that /etc/inet/hosts was saved
+ self.assert_(os.path.isfile(os.path.join(self.ppim.pkg_img_path,
+ "save/etc/inet/hosts")))
+
class TestCalculateSize(unittest.TestCase):
""" test case for testing the calculation of the size of pkg_image_path
@@ -229,7 +186,7 @@
engine_test_utils.reset_engine()
-class TestSaveFiles(unittest.TestCase):
+class TestCustomizeLiveCDFiles(unittest.TestCase):
""" test case for testing the saving of important files in /save
"""
@@ -239,6 +196,8 @@
engine_test_utils.get_new_engine_instance()
self.ppim = LiveCDPrePkgImgMod("Test PPIM")
self.pi_filelist = [
+ "etc/shadow", "etc/system", "etc/default/dhcpagent",
+ "etc/inet/hosts",
"usr/share/dbus-1/services/", "etc/gconf/schemas/",
"usr/share/gnome/autostart/", "etc/xdg/autostart/",
"etc/xdg/autostart/updatemanagernotifier.desktop",
@@ -250,12 +209,26 @@
self.ppim.pkg_img_path = testlib.create_filesystem(*self.pi_filelist)
self.ppim.save_path = os.path.join(self.ppim.pkg_img_path, "save")
+ # copy /usr/lib/libc.so and /usr/lib/libc.so.1 to the pkg_img_path
+ os.makedirs(os.path.join(self.ppim.pkg_img_path, "usr/lib"))
+ shutil.copy2("/usr/lib/libc.so",
+ os.path.join(self.ppim.pkg_img_path, "usr/lib/"))
+
+ shutil.copy2("/usr/lib/libc.so.1",
+ os.path.join(self.ppim.pkg_img_path, "usr/lib/"))
+
+ # copy /etc/passwd and shadow to the pkg_img_path
+ shutil.copy2("/etc/passwd", os.path.join(self.ppim.pkg_img_path,
+ "etc/passwd"))
+ shutil.copy2("/etc/shadow", os.path.join(self.ppim.pkg_img_path,
+ "etc/shadow"))
+
def tearDown(self):
shutil.rmtree(self.ppim.pkg_img_path, ignore_errors=True)
engine_test_utils.reset_engine()
- def test_save_files(self):
- self.ppim.save_files()
+ def test_customize_livecd_config_files(self):
+ self.ppim.customize_config_files()
# verify each file or directory is in /save
for entry in self.pi_filelist:
@@ -321,7 +294,7 @@
engine_test_utils.reset_engine()
def test_configure_smf_default_hostname(self):
- # insert a system/identity:node serivce into the var/svc manifest
+ # insert a system/identity:node service into the var/svc manifest
manifest = os.path.join(self.ppim.pkg_img_path,
"var/svc/manifest/system/var_stub.xml")
with open(manifest, "r") as fh:
--- a/usr/src/lib/install_ict/cleanup_cpio_install.py Mon May 07 17:32:38 2012 -0700
+++ b/usr/src/lib/install_ict/cleanup_cpio_install.py Wed May 09 08:51:52 2012 +0200
@@ -56,10 +56,10 @@
system
- Remove miscellanous directory trees used as work areas during
installation
- - Relocate configuration files from the save directory
- Remove install-specific packages that are not needed by the installed
system
- Reset pkg(1) image UUID for preferred publisher
+ - Relocate configuration files from the save directory
'''
def __init__(self, name):
'''Initializes the class
@@ -196,27 +196,6 @@
if not dry_run:
os.rmdir(os.path.join(root, work_dir))
- # Relocate configuration files from the save directory
- savedir = os.path.join(self.target_dir, 'save')
- if os.path.exists(savedir):
- self.logger.debug('Executing: Relocate configuration files')
- for root, dirs, files in os.walk(savedir, topdown=False):
- if not files:
- continue
-
- target = root.replace('/save', '')
- if not dry_run:
- if not os.access(target, os.F_OK):
- os.makedirs(target, 0755)
-
- for name in files:
- move_file = os.path.join(root, name)
- self.logger.debug('Moving %s to %s', move_file, target)
- if not dry_run:
- shutil.copy2(move_file, target)
-
- shutil.rmtree(savedir)
-
if not dry_run:
try:
api_inst = api.ImageInterface(self.target_dir,
@@ -307,6 +286,44 @@
publisher = api_inst.get_highest_ranked_publisher()
publisher.reset_client_uuid()
+ #
+ # Now that all pkg(5) operations are finished, restore files
+ # from the save directory. This order of steps is intentional,
+ # as pkg(5) operations may have wanted to modify files which
+ # are to be restored.
+ #
+ # As an example, uninstalling media/internal package removed
+ # media specific 'jack' user from the target system. That (among other
+ # things) removed related entry from shadow(4) file (now to be
+ # restored from save area).
+ #
+ savedir = os.path.join(self.target_dir, 'save')
+ if os.path.exists(savedir):
+ self.logger.debug('Executing: Relocate configuration files')
+ for root, dirs, files in os.walk(savedir, topdown=False):
+ if not files:
+ continue
+
+ target = root.replace('/save', '')
+ if not dry_run:
+ if not os.access(target, os.F_OK):
+ os.makedirs(target, 0755)
+
+ for name in files:
+ src_file = os.path.join(root, name)
+ dst_file = os.path.join(target, name)
+ self.logger.debug('Moving %s to %s', src_file, dst_file)
+ if not dry_run:
+ #
+ # Use shutil.move(), as it transfers also file
+ # permissions and ownership. Assuming that files
+ # in save area were created with desired permissions
+ # and ownership.
+ #
+ shutil.move(src_file, dst_file)
+
+ shutil.rmtree(savedir)
+
# Remove the files and directories in the cleanup_list
self.logger.debug('Executing: Cleanup of %s', self.cleanup_list)
for cleanup_name in self.cleanup_list: