a first stab at upgrading nodes
authorThierry Parmentelat <thierry.parmentelat@inria.fr>
Tue, 23 Jun 2015 11:22:46 +0000 (13:22 +0200)
committerThierry Parmentelat <thierry.parmentelat@inria.fr>
Tue, 23 Jun 2015 11:22:46 +0000 (13:22 +0200)
not working yet, but I need to build all this

source/BootManager.py
source/steps/InstallBootstrapFS.py

index 2d9db0b..55686ea 100755 (executable)
@@ -276,13 +276,15 @@ class BootManager:
             # 'boot' state and chainboot into the production system
             if not CheckHardwareRequirements.Run(self.VARS, self.LOG):
                 self.VARS['RUN_LEVEL'] = 'failboot'
-                raise BootManagerException, "Hardware requirements not met."
+                raise BootManagerException("Hardware requirements not met.")
 
             # runinstaller
             InstallInit.Run(self.VARS, self.LOG)
+            # do not erase disks in upgrade mode
             if not upgrade:
                 InstallPartitionDisks.Run(self.VARS, self.LOG)
-            InstallBootstrapFS.Run(self.VARS, self.LOG)
+            # pass upgrade boolean to this step so we can do extra cleanup
+            InstallBootstrapFS.Run(self.VARS, upgrade, self.LOG)
             InstallWriteConfig.Run(self.VARS, self.LOG)
             InstallUninitHardware.Run(self.VARS, self.LOG)
             self.VARS['BOOT_STATE'] = 'boot'
index 0587057..8875394 100644 (file)
@@ -19,9 +19,16 @@ import BootServerRequest
 import BootAPI
 
 
-def Run(vars, log):
+def Run(vars, upgrade, log):
     """
     Download core + extensions bootstrapfs tarballs and install on the hard drive
+
+    the upgrade boolean is True when we are upgrading a node root install while 
+    preserving its slice contents; in that case we just perform extra cleanup
+    before unwrapping the bootstrapfs
+    this is because the running system may have extraneous files
+    that is to say, files that are *not* present in the bootstrapfs
+    and that can impact/clobber the resulting upgrade
     
     Expect the following variables from the store:
     SYSIMG_PATH          the path where the system image will be mounted
@@ -36,7 +43,7 @@ def Run(vars, log):
                          are mounted.
     """
 
-    log.write("\n\nStep: Install: bootstrapfs tarball.\n")
+    log.write("\n\nStep: Install: bootstrapfs tarball (upgrade={}).\n".format(upgrade))
 
     # make sure we have the variables we need
     try:
@@ -101,6 +108,13 @@ def Run(vars, log):
     # this is now retrieved in GetAndUpdateNodeDetails
     nodefamily = vars['nodefamily']
     extensions = vars['extensions']
+
+    # in upgrade mode: we need to cleanup the disk to make
+    # it safe to just untar the new bootstrapfs tarball again
+    # on top of the hard drive
+    if upgrade:
+        CleanupSysimgBeforeUpgrade(SYSIMG_PATH, nodefamily, log)
+
     # the 'plain' option is for tests mostly
     plain = vars['plain']
     if plain:
@@ -214,7 +228,57 @@ def Run(vars, log):
     now = time.strftime("%Y-%b-%d @ %H:%M %Z", time.gmtime())
     stamp.write("Hard drive installed by BootManager {}\n".format(VERSION))
     stamp.write("Finished extraction of bootstrapfs on {}\n".format(now))
+    # do not modify this, the upgrade code uses this line for checking compatibility
     stamp.write("Using nodefamily {}\n".format(nodefamily))
     stamp.close()
 
     return 1
+
+# the upgrade hook
+def CleanupSysimgBeforeUpgrade(sysimg, target_nodefamily, log):
+
+    areas_to_cleanup = [
+        '/usr/lib',
+        '/var',
+        '/etc',
+        '/boot',
+    ]
+
+    target_pldistro, target_fcdistro, target_arch = target_nodefamily.split('-')
+
+    # minimal check : not all configurations are possible...
+
+    installed_pldistro, installed_fcdistro, installed_arch = None, None, None
+    installed_virt = None
+    prefix = "Using nodefamily "
+    try:
+        with open("{}/bm-install.txt".format(sysimg)) as infile:
+            for line in infile:
+                if line.startswith(prefix):
+                    installed_nodefamily = line.replace(prefix,"").strip()
+                    installed_pldistro, installed_fcdistro, installed_arch = installed_nodefamily.split('-')
+        with open("{}/etc/planetlab/virt".format(sysimg)) as infile:
+            installed_virt = infile.read().strip()
+    except Exception as e:
+        print_exc()
+        raise BootManagerException("Could not retrieve data about previous installation - cannot upgrade")
+
+    # moving from vservers to lxc also means another filesystem
+    # so plain reinstall is the only option
+    if installed_virt != 'lxc':
+        raise BootManagerException("Can only upgrade nodes running lxc containers (vservers not supported)")
+
+    # changing arch is not reasonable either
+    if target_arch != installed_arch:
+        raise BootManagerException("Cannot upgrade from arch={} to arch={}"
+                                   .format(installed_arch, target_arch))
+
+    if target_pldistro != installed_pldistro:
+        log.write("\nWARNING: upgrading across pldistros {} to {} - might not work well..\n"
+                  .format(installed_pldistro, target_pldistro))
+    
+    # otherwise at this point we do not do any more advanced checking
+    log.write("\n\nPseudo step CleanupSysimgBeforeUpgrade : cleaning up hard drive\n")
+    
+    for area in areas_to_cleanup:
+        utils.sysexec("rm -rf {}/{}".format(sysimg, area))