Replaces filesystems in /etc/fstab with their new UUIDs.

Signed-off-by: Ben Lipton <[email protected]>
---
 instance-p2v-target/Makefile.am                  |    9 ++-
 instance-p2v-target/fixes/10_fix_fstab           |   23 ++++++
 instance-p2v-target/fixes/fixlib/fix_fstab.py    |   83 +++++++++++++++++++++
 instance-p2v-target/test/fix_fstab_test.py       |   84 ++++++++++++++++++++++
 instance-p2v-target/test/testdata/fstab_uuid_in  |   14 ++++
 instance-p2v-target/test/testdata/fstab_uuid_out |   14 ++++
 6 files changed, 225 insertions(+), 2 deletions(-)
 create mode 100755 instance-p2v-target/fixes/10_fix_fstab
 create mode 100644 instance-p2v-target/fixes/fixlib/fix_fstab.py
 create mode 100755 instance-p2v-target/test/fix_fstab_test.py
 create mode 100644 instance-p2v-target/test/testdata/fstab_uuid_in
 create mode 100644 instance-p2v-target/test/testdata/fstab_uuid_out

diff --git a/instance-p2v-target/Makefile.am b/instance-p2v-target/Makefile.am
index b201da1..b4a8688 100644
--- a/instance-p2v-target/Makefile.am
+++ b/instance-p2v-target/Makefile.am
@@ -19,9 +19,11 @@ dist_os_DATA = ganeti_api_version variants.list
 os_SCRIPTS = common.sh
 
 dist_sbin_SCRIPTS = scripts/make_ramboot_initrd.py
-dist_fixes_SCRIPTS =
+dist_fixes_SCRIPTS = \
+       fixes/10_fix_fstab
 
 dist_fixlib_DATA = \
+       fixes/fixlib/fix_fstab.py \
        fixes/fixlib/__init__.py
 
 EXTRA_DIST = $(patsubst %,%.in,$(subst_files)) \
@@ -32,11 +34,14 @@ EXTRA_DIST = $(patsubst %,%.in,$(subst_files)) \
 abs_top_srcdir = @abs_top_srcdir@
 srcdir = $(abs_top_srcdir)/instance-p2v-target
 dist_TESTS = \
-       test/make_ramboot_initrd_test.py
+       test/make_ramboot_initrd_test.py \
+       test/fix_fstab_test.py
 TESTS = $(dist_TESTS)
 TESTS_ENVIRONMENT = \
        PYTHONPATH=$(srcdir)/scripts:$(srcdir)/fixes SRCDIR=$(srcdir)
 test_extras = \
+       test/testdata/fstab_uuid_in \
+       test/testdata/fstab_uuid_out \
        test/testdata/movetoram
 
 do_subst = sed \
diff --git a/instance-p2v-target/fixes/10_fix_fstab 
b/instance-p2v-target/fixes/10_fix_fstab
new file mode 100755
index 0000000..366491b
--- /dev/null
+++ b/instance-p2v-target/fixes/10_fix_fstab
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2011 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+from fixlib import fix_fstab
+
+if __name__ == "__main__":
+  fix_fstab.FixFstab()
diff --git a/instance-p2v-target/fixes/fixlib/fix_fstab.py 
b/instance-p2v-target/fixes/fixlib/fix_fstab.py
new file mode 100644
index 0000000..3e652c5
--- /dev/null
+++ b/instance-p2v-target/fixes/fixlib/fix_fstab.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2011 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+import subprocess
+import re
+
+import fixlib
+
+def FixFstab(fname_in="/target/etc/fstab", fname_out="/target/etc/fstab"):
+  """Alter the fstab to refer to new filesystems.
+
+  This function edits the fstab file found at fname_in, locating the filesystem
+  mounted on / and replacing the device with the string UUID=<new_uuid>.
+
+  @type fname_in: string
+  @param fname_in: The fstab file to read.
+  @type fname_out: string
+  @param fname_out: Where to write the modified file.
+
+  """
+  # TODO(benlipton): This is totally xen-specific. Better would be to use the
+  # first that exists of /dev/{xvda,vda,sda}.
+
+  # Note: This also assumes a particular partition layout (root partition on
+  # /dev/${device}1, swap partition on /dev/${device}2). This is only a problem
+  # if we allow configurable partition layouts, so we'll let it stand for now,
+  # but for the future, the bootstrap OS's /etc/mtab probably contains enough
+  # information to reconstruct the whole partitioning scheme.
+  uuids = {}
+  fstypes = {}
+  p = subprocess.Popen(["blkid"], stdout=subprocess.PIPE)
+  for line in p.communicate()[0].split('\n'):
+    parts = re.match("/dev/(xvda[0-9]): UUID=\"([-a-z0-9]+)\" TYPE=\"(.+)\"",
+                     line)
+    if parts:
+      partname, uuid, fstype = parts.groups()
+      uuids[partname] = uuid
+      fstypes[partname] = fstype
+
+  if "xvda1" not in uuids:
+    print uuids
+    raise fixlib.FixError("Could not determine UUID of root filesystem."
+                          " /etc/fstab may need to be edited by hand")
+
+  fstab_file = open(fname_in, "r")
+  new_fstab = ""
+  for line in fstab_file:
+    parts = line.split()
+    if len(parts) >= 2 and parts[0][0] != "#":
+      if parts[1] == "/":
+        parts[0] = "UUID=%s" % uuids["xvda1"]
+        parts[2] = fstypes["xvda1"]
+        line = "\t".join(parts) + "\n"
+      elif parts[2] == "swap":
+        parts[0] = "UUID=%s" % uuids["xvda2"]
+        line = "\t".join(parts) + "\n"
+    new_fstab += line
+  fstab_file.close()
+  fstab_file = open(fname_out, "w")
+  fstab_file.write(new_fstab)
+  fstab_file.close()
+
+def main():
+  FixFstab()
+
+if __name__ == "__main__":
+  main()
diff --git a/instance-p2v-target/test/fix_fstab_test.py 
b/instance-p2v-target/test/fix_fstab_test.py
new file mode 100755
index 0000000..36db907
--- /dev/null
+++ b/instance-p2v-target/test/fix_fstab_test.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2011 Google Inc.
+
+"""Tests for fix_fstab."""
+
+
+import mox
+import os
+import tempfile
+import unittest
+
+from fixlib import fix_fstab
+
+
+class FixFstabTest(unittest.TestCase):
+  SRCDIR = os.environ.get("SRCDIR", ".")
+  TESTDATA = os.path.join(SRCDIR, "test", "testdata")
+
+  def setUp(self):
+    self.mox = mox.Mox()
+
+  def tearDown(self):
+    self.mox.UnsetStubs()
+    self.mox.ResetAll()
+
+  def testFixFstabReplacesRootLine(self):
+    # stub out blkid call
+    mock_popen = self.mox.CreateMock(fix_fstab.subprocess.Popen)
+
+    self.mox.StubOutWithMock(fix_fstab.subprocess, "Popen",
+                             use_mock_anything=True)
+    call = fix_fstab.subprocess.Popen(["blkid"],
+                                       stdout=fix_fstab.subprocess.PIPE)
+    call.AndReturn(mock_popen)
+    blkid_str = ("/dev/xvda1: UUID=\"11111111-1111-1111-1111-111111111111\""
+                 " TYPE=\"ext3\"\n/dev/xvda2: UUID=\"22222222-2222-2222-2222"
+                 "-222222222222\" TYPE=\"swap\"\n")
+    mock_popen.communicate().AndReturn((blkid_str, ""))
+
+    self.mox.ReplayAll()
+
+    fname_in = os.path.join(self.TESTDATA, "fstab_uuid_in")
+    handle_out, fname_out = tempfile.mkstemp()
+    os.close(handle_out)
+    fix_fstab.FixFstab(fname_in, fname_out)
+
+    out = open(fname_out, "r")
+    desired = open(os.path.join(self.TESTDATA, "fstab_uuid_out"), "r")
+    out_data = out.read()
+    desired_data = desired.read()
+    out.close()
+    desired.close()
+
+    self.assertEqual(desired_data, out_data)
+
+    self.mox.VerifyAll()
+
+
+  def testFixFstabHandlesDeviceMissing(self):
+    # stub out blkid call
+    mock_popen = self.mox.CreateMock(fix_fstab.subprocess.Popen)
+
+    self.mox.StubOutWithMock(fix_fstab.subprocess, "Popen",
+                             use_mock_anything=True)
+    call = fix_fstab.subprocess.Popen(["blkid"],
+                                       stdout=fix_fstab.subprocess.PIPE)
+    call.AndReturn(mock_popen)
+    mock_popen.communicate().AndReturn(("", ""))
+
+    self.mox.ReplayAll()
+
+    fname_in = os.path.join(self.TESTDATA, "fstab_uuid_in")
+    handle_out, fname_out = tempfile.mkstemp()
+    os.close(handle_out)
+
+    self.assertRaises(fix_fstab.fixlib.FixError, fix_fstab.FixFstab,
+                      fname_in, fname_out)
+
+    self.mox.VerifyAll()
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/instance-p2v-target/test/testdata/fstab_uuid_in 
b/instance-p2v-target/test/testdata/fstab_uuid_in
new file mode 100644
index 0000000..7e02ad6
--- /dev/null
+++ b/instance-p2v-target/test/testdata/fstab_uuid_in
@@ -0,0 +1,14 @@
+# /etc/fstab: static file system information.
+#
+# Use 'blkid' to print the universally unique identifier for a
+# device; this may be used with UUID= as a more robust way to name devices
+# that works even if disks are added and removed. See fstab(5).
+#
+# <file system> <mount point>   <type>  <options>       <dump>  <pass>
+proc            /proc           proc    defaults        0       0
+# / was on /dev/sda1 during installation
+UUID=00000000-0000-0000-0000-000000000000 /               ext3    
errors=remount-ro 0       1
+# swap was on /dev/sda5 during installation
+UUID=55555555-5555-5555-5555-555555555555 none            swap    sw           
   0       0
+/dev/scd0       /media/cdrom0   udf,iso9660 user,noauto     0       0
+/dev/fd0        /media/floppy0  auto    rw,user,noauto  0       0
diff --git a/instance-p2v-target/test/testdata/fstab_uuid_out 
b/instance-p2v-target/test/testdata/fstab_uuid_out
new file mode 100644
index 0000000..d20dbbf
--- /dev/null
+++ b/instance-p2v-target/test/testdata/fstab_uuid_out
@@ -0,0 +1,14 @@
+# /etc/fstab: static file system information.
+#
+# Use 'blkid' to print the universally unique identifier for a
+# device; this may be used with UUID= as a more robust way to name devices
+# that works even if disks are added and removed. See fstab(5).
+#
+# <file system> <mount point>   <type>  <options>       <dump>  <pass>
+proc            /proc           proc    defaults        0       0
+# / was on /dev/sda1 during installation
+UUID=11111111-1111-1111-1111-111111111111      /       ext3    
errors=remount-ro       0       1
+# swap was on /dev/sda5 during installation
+UUID=22222222-2222-2222-2222-222222222222      none    swap    sw      0       0
+/dev/scd0       /media/cdrom0   udf,iso9660 user,noauto     0       0
+/dev/fd0        /media/floppy0  auto    rw,user,noauto  0       0
-- 
1.7.3.1

Reply via email to