summaryrefslogtreecommitdiff
path: root/azurelinuxagent/daemon/resourcedisk/default.py
diff options
context:
space:
mode:
Diffstat (limited to 'azurelinuxagent/daemon/resourcedisk/default.py')
-rw-r--r--azurelinuxagent/daemon/resourcedisk/default.py229
1 files changed, 158 insertions, 71 deletions
diff --git a/azurelinuxagent/daemon/resourcedisk/default.py b/azurelinuxagent/daemon/resourcedisk/default.py
index d2e400a..18ce884 100644
--- a/azurelinuxagent/daemon/resourcedisk/default.py
+++ b/azurelinuxagent/daemon/resourcedisk/default.py
@@ -28,23 +28,26 @@ import azurelinuxagent.common.utils.shellutil as shellutil
from azurelinuxagent.common.exception import ResourceDiskError
from azurelinuxagent.common.osutil import get_osutil
-DATALOSS_WARNING_FILE_NAME="DATALOSS_WARNING_README.txt"
-DATA_LOSS_WARNING="""\
+DATALOSS_WARNING_FILE_NAME = "DATALOSS_WARNING_README.txt"
+DATA_LOSS_WARNING = """\
WARNING: THIS IS A TEMPORARY DISK.
Any data stored on this drive is SUBJECT TO LOSS and THERE IS NO WAY TO RECOVER IT.
Please do not use this disk for storing any personal or application data.
-For additional details to please refer to the MSDN documentation at : http://msdn.microsoft.com/en-us/library/windowsazure/jj672979.aspx
+For additional details to please refer to the MSDN documentation at :
+http://msdn.microsoft.com/en-us/library/windowsazure/jj672979.aspx
"""
+
class ResourceDiskHandler(object):
def __init__(self):
self.osutil = get_osutil()
+ self.fs = conf.get_resourcedisk_filesystem()
def start_activate_resource_disk(self):
- disk_thread = threading.Thread(target = self.run)
+ disk_thread = threading.Thread(target=self.run)
disk_thread.start()
def run(self):
@@ -59,18 +62,18 @@ class ResourceDiskHandler(object):
logger.info("Activate resource disk")
try:
mount_point = conf.get_resourcedisk_mountpoint()
- fs = conf.get_resourcedisk_filesystem()
- mount_point = self.mount_resource_disk(mount_point, fs)
- warning_file = os.path.join(mount_point, DATALOSS_WARNING_FILE_NAME)
+ mount_point = self.mount_resource_disk(mount_point)
+ warning_file = os.path.join(mount_point,
+ DATALOSS_WARNING_FILE_NAME)
try:
fileutil.write_file(warning_file, DATA_LOSS_WARNING)
except IOError as e:
- logger.warn("Failed to write data loss warnning:{0}", e)
+ logger.warn("Failed to write data loss warning:{0}", e)
return mount_point
except ResourceDiskError as e:
logger.error("Failed to mount resource disk {0}", e)
add_event(name="WALA", is_success=False, message=ustr(e),
- op=WALAEventOperation.ActivateResourceDisk)
+ op=WALAEventOperation.ActivateResourceDisk)
def enable_swap(self, mount_point):
logger.info("Enable swap")
@@ -80,67 +83,134 @@ class ResourceDiskHandler(object):
except ResourceDiskError as e:
logger.error("Failed to enable swap {0}", e)
- def mount_resource_disk(self, mount_point, fs):
+ def mount_resource_disk(self, mount_point):
device = self.osutil.device_for_ide_port(1)
if device is None:
raise ResourceDiskError("unable to detect disk topology")
- device = "/dev/" + device
- mountlist = shellutil.run_get_output("mount")[1]
- existing = self.osutil.get_mount_point(mountlist, device)
+ device = "/dev/{0}".format(device)
+ partition = device + "1"
+ mount_list = shellutil.run_get_output("mount")[1]
+ existing = self.osutil.get_mount_point(mount_list, device)
- if(existing):
- logger.info("Resource disk {0}1 is already mounted", device)
+ if existing:
+ logger.info("Resource disk [{0}] is already mounted [{1}]",
+ partition,
+ existing)
return existing
fileutil.mkdir(mount_point, mode=0o755)
-
- logger.info("Detect GPT...")
- partition = device + "1"
+ logger.info("Examining partition table")
ret = shellutil.run_get_output("parted {0} print".format(device))
if ret[0]:
- raise ResourceDiskError("({0}) {1}".format(device, ret[1]))
+ raise ResourceDiskError("Could not determine partition info for "
+ "{0}: {1}".format(device, ret[1]))
+
+ force_option = 'F'
+ if self.fs == 'xfs':
+ force_option = 'f'
+ mkfs_string = "mkfs.{0} {1} -{2}".format(self.fs, partition, force_option)
if "gpt" in ret[1]:
- logger.info("GPT detected")
- logger.info("Get GPT partitions")
- parts = [x for x in ret[1].split("\n") if re.match("^\s*[0-9]+", x)]
- logger.info("Found more than {0} GPT partitions.", len(parts))
+ logger.info("GPT detected, finding partitions")
+ parts = [x for x in ret[1].split("\n") if
+ re.match("^\s*[0-9]+", x)]
+ logger.info("Found {0} GPT partition(s).", len(parts))
if len(parts) > 1:
- logger.info("Remove old GPT partitions")
+ logger.info("Removing old GPT partitions")
for i in range(1, len(parts) + 1):
- logger.info("Remove partition: {0}", i)
+ logger.info("Remove partition {0}", i)
shellutil.run("parted {0} rm {1}".format(device, i))
- logger.info("Create a new GPT partition using entire disk space")
+ logger.info("Creating new GPT partition")
shellutil.run("parted {0} mkpart primary 0% 100%".format(device))
- logger.info("Format partition: {0} with fstype {1}",partition,fs)
- shellutil.run("mkfs." + fs + " " + partition + " -F")
+ logger.info("Format partition [{0}]", mkfs_string)
+ shellutil.run(mkfs_string)
else:
- logger.info("GPT not detected")
- logger.info("Check fstype")
- ret = shellutil.run_get_output("sfdisk -q -c {0} 1".format(device))
- if ret[1].rstrip() == "7" and fs != "ntfs":
- logger.info("The partition is formatted with ntfs")
- logger.info("Format partition: {0} with fstype {1}",partition,fs)
- shellutil.run("sfdisk -c {0} 1 83".format(device))
- shellutil.run("mkfs." + fs + " " + partition + " -F")
-
- logger.info("Mount resource disk")
- ret = shellutil.run("mount {0} {1}".format(partition, mount_point),
- chk_err=False)
+ logger.info("GPT not detected, determining filesystem")
+ ret = self.change_partition_type(suppress_message=True, option_str="{0} 1".format(device))
+ ptype = ret[1].strip()
+ if ptype == "7" and self.fs != "ntfs":
+ logger.info("The partition is formatted with ntfs, updating "
+ "partition type to 83")
+ self.change_partition_type(suppress_message=False, option_str="{0} 1 83".format(device))
+ logger.info("Format partition [{0}]", mkfs_string)
+ shellutil.run(mkfs_string)
+ else:
+ logger.info("The partition type is {0}", ptype)
+
+ mount_options = conf.get_resourcedisk_mountoptions()
+ mount_string = self.get_mount_string(mount_options,
+ partition,
+ mount_point)
+ logger.info("Mount resource disk [{0}]", mount_string)
+ ret = shellutil.run(mount_string, chk_err=False)
if ret:
- logger.warn("Failed to mount resource disk. Retry mounting")
- shellutil.run("mkfs." + fs + " " + partition + " -F")
- ret = shellutil.run("mount {0} {1}".format(partition, mount_point))
+ # Some kernels seem to issue an async partition re-read after a
+ # 'parted' command invocation. This causes mount to fail if the
+ # partition re-read is not complete by the time mount is
+ # attempted. Seen in CentOS 7.2. Force a sequential re-read of
+ # the partition and try mounting.
+ logger.warn("Failed to mount resource disk. "
+ "Retry mounting after re-reading partition info.")
+ if shellutil.run("sfdisk -R {0}".format(device), chk_err=False):
+ shellutil.run("blockdev --rereadpt {0}".format(device),
+ chk_err=False)
+ ret = shellutil.run(mount_string, chk_err=False)
if ret:
- raise ResourceDiskError("({0}) {1}".format(partition, ret))
-
- logger.info("Resource disk ({0}) is mounted at {1} with fstype {2}",
- device, mount_point, fs)
+ logger.warn("Failed to mount resource disk. "
+ "Attempting to format and retry mount.")
+ shellutil.run(mkfs_string)
+ ret = shellutil.run(mount_string)
+ if ret:
+ raise ResourceDiskError("Could not mount {0} "
+ "after syncing partition table: "
+ "{1}".format(partition, ret))
+
+ logger.info("Resource disk {0} is mounted at {1} with {2}",
+ device,
+ mount_point,
+ self.fs)
return mount_point
+ def change_partition_type(self, suppress_message, option_str):
+ """
+ use sfdisk to change partition type.
+ First try with --part-type; if fails, fall back to -c
+ """
+
+ command_to_use = '--part-type'
+ input = "sfdisk {0} {1} {2}".format(command_to_use, '-f' if suppress_message else '', option_str)
+ err_code, output = shellutil.run_get_output(input, chk_err=False, log_cmd=True)
+
+ # fall back to -c
+ if err_code != 0:
+ logger.info("sfdisk with --part-type failed [{0}], retrying with -c", err_code)
+ command_to_use = '-c'
+ input = "sfdisk {0} {1} {2}".format(command_to_use, '-f' if suppress_message else '', option_str)
+ err_code, output = shellutil.run_get_output(input, log_cmd=True)
+
+ if err_code == 0:
+ logger.info('{0} succeeded',
+ input)
+ else:
+ logger.error('{0} failed [{1}: {2}]',
+ input,
+ err_code,
+ output)
+
+ return err_code, output
+
+ @staticmethod
+ def get_mount_string(mount_options, partition, mount_point):
+ if mount_options is not None:
+ return 'mount -o {0} {1} {2}'.format(mount_options,
+ partition,
+ mount_point)
+ else:
+ return 'mount {0} {1}'.format(partition, mount_point)
+
def create_swap_space(self, mount_point, size_mb):
size_kb = size_mb * 1024
size = size_kb * 1024
@@ -166,13 +236,17 @@ class ResourceDiskHandler(object):
def mkfile(self, filename, nbytes):
"""
- Create a non-sparse file of that size. Deletes and replaces existing file.
+ Create a non-sparse file of that size. Deletes and replaces existing
+ file.
- To allow efficient execution, fallocate will be tried first. This includes
- ``os.posix_fallocate`` on Python 3.3+ (unix) and the ``fallocate`` command
+ To allow efficient execution, fallocate will be tried first. This
+ includes
+ ``os.posix_fallocate`` on Python 3.3+ (unix) and the ``fallocate``
+ command
in the popular ``util-linux{,-ng}`` package.
- A dd fallback will be tried too. When size < 64M, perform single-pass dd.
+ A dd fallback will be tried too. When size < 64M, perform
+ single-pass dd.
Otherwise do two-pass dd.
"""
@@ -185,35 +259,48 @@ class ResourceDiskHandler(object):
if os.path.isfile(filename):
os.remove(filename)
- # os.posix_fallocate
- if sys.version_info >= (3, 3):
- # Probable errors:
- # - OSError: Seen on Cygwin, libc notimpl?
- # - AttributeError: What if someone runs this under...
- with open(filename, 'w') as f:
- try:
- os.posix_fallocate(f.fileno(), 0, nbytes)
- return 0
- except:
- # Not confident with this thing, just keep trying...
- pass
-
- # fallocate command
+ # If file system is xfs, use dd right away as we have been reported that
+ # swap enabling fails in xfs fs when disk space is allocated with fallocate
+ ret = 0
fn_sh = shellutil.quote((filename,))
- ret = shellutil.run(u"fallocate -l {0} {1}".format(nbytes, fn_sh))
- if ret != 127: # 127 = command not found
- return ret
+ if self.fs != 'xfs':
+ # os.posix_fallocate
+ if sys.version_info >= (3, 3):
+ # Probable errors:
+ # - OSError: Seen on Cygwin, libc notimpl?
+ # - AttributeError: What if someone runs this under...
+ with open(filename, 'w') as f:
+ try:
+ os.posix_fallocate(f.fileno(), 0, nbytes)
+ return 0
+ except:
+ # Not confident with this thing, just keep trying...
+ pass
+
+ # fallocate command
+ ret = shellutil.run(
+ u"umask 0077 && fallocate -l {0} {1}".format(nbytes, fn_sh))
+ if ret == 0:
+ return ret
+
+ logger.info("fallocate unsuccessful, falling back to dd")
# dd fallback
dd_maxbs = 64 * 1024 ** 2
- dd_cmd = "dd if=/dev/zero bs={0} count={1} conv=notrunc of={2}"
+ dd_cmd = "umask 0077 && dd if=/dev/zero bs={0} count={1} " \
+ "conv=notrunc of={2}"
blocks = int(nbytes / dd_maxbs)
if blocks > 0:
- ret = shellutil.run(dd_cmd.format(dd_maxbs, fn_sh, blocks)) << 8
+ ret = shellutil.run(dd_cmd.format(dd_maxbs, blocks, fn_sh)) << 8
remains = int(nbytes % dd_maxbs)
if remains > 0:
- ret += shellutil.run(dd_cmd.format(remains, fn_sh, 1))
+ ret += shellutil.run(dd_cmd.format(remains, 1, fn_sh))
+
+ if ret == 0:
+ logger.info("dd successful")
+ else:
+ logger.error("dd unsuccessful")
return ret