trove/reddwarf/guestagent/volume.py

158 lines
6.0 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack, LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from reddwarf.openstack.common import log as logging
import os
import pexpect
from reddwarf.common import cfg
from reddwarf.common import utils
from reddwarf.common.exception import GuestError
from reddwarf.common.exception import ProcessExecutionError
TMP_MOUNT_POINT = "/mnt/volume"
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class VolumeDevice(object):
def __init__(self, device_path):
self.device_path = device_path
def migrate_data(self, mysql_base):
""" Synchronize the data from the mysql directory to the new volume """
# Use sudo to have access to this spot.
utils.execute("sudo", "mkdir", "-p", TMP_MOUNT_POINT)
self._tmp_mount(TMP_MOUNT_POINT)
if not mysql_base[-1] == '/':
mysql_base = "%s/" % mysql_base
utils.execute("sudo", "rsync", "--safe-links", "--perms",
"--recursive", "--owner", "--group", "--xattrs",
"--sparse", mysql_base, TMP_MOUNT_POINT)
self.unmount()
def _check_device_exists(self):
"""Check that the device path exists.
Verify that the device path has actually been created and can report
it's size, only then can it be available for formatting, retry
num_tries to account for the time lag.
"""
try:
num_tries = CONF.num_tries
utils.execute('sudo', 'blockdev', '--getsize64', self.device_path,
attempts=num_tries)
except ProcessExecutionError:
raise GuestError("InvalidDevicePath(path=%s)" % self.device_path)
def _check_format(self):
"""Checks that an unmounted volume is formatted."""
child = pexpect.spawn("sudo dumpe2fs %s" % self.device_path)
try:
i = child.expect(['has_journal', 'Wrong magic number'])
if i == 0:
return
volume_fstype = CONF.volume_fstype
raise IOError('Device path at %s did not seem to be %s.' %
(self.device_path, volume_fstype))
except pexpect.EOF:
raise IOError("Volume was not formatted.")
child.expect(pexpect.EOF)
def _format(self):
"""Calls mkfs to format the device at device_path."""
volume_fstype = CONF.volume_fstype
format_options = CONF.format_options
cmd = "sudo mkfs -t %s %s %s" % (volume_fstype,
format_options, self.device_path)
volume_format_timeout = CONF.volume_format_timeout
child = pexpect.spawn(cmd, timeout=volume_format_timeout)
# child.expect("(y,n)")
# child.sendline('y')
child.expect(pexpect.EOF)
def format(self):
"""Formats the device at device_path and checks the filesystem."""
self._check_device_exists()
self._format()
self._check_format()
def mount(self, mount_point):
"""Mounts, and writes to fstab."""
mount_point = VolumeMountPoint(self.device_path, mount_point)
mount_point.mount()
mount_point.write_to_fstab()
#TODO(tim.simpson): Are we using this?
def resize_fs(self):
"""Resize the filesystem on the specified device"""
self._check_device_exists()
try:
utils.execute("sudo", "resize2fs", self.device_path)
except ProcessExecutionError as err:
LOG.error(err)
raise GuestError("Error resizing the filesystem: %s" %
self.device_path)
def _tmp_mount(self, mount_point):
"""Mounts, but doesn't save to fstab."""
mount_point = VolumeMountPoint(self.device_path, mount_point)
mount_point.mount() # Don't save to fstab.
def unmount(self):
if os.path.exists(self.device_path):
cmd = "sudo umount %s" % self.device_path
child = pexpect.spawn(cmd)
child.expect(pexpect.EOF)
class VolumeMountPoint(object):
def __init__(self, device_path, mount_point):
self.device_path = device_path
self.mount_point = mount_point
self.volume_fstype = CONF.volume_fstype
self.mount_options = CONF.mount_options
def mount(self):
if not os.path.exists(self.mount_point):
os.makedirs(self.mount_point)
LOG.debug("Adding volume. Device path:%s, mount_point:%s, "
"volume_type:%s, mount options:%s" %
(self.device_path, self.mount_point, self.volume_fstype,
self.mount_options))
cmd = ("sudo mount -t %s -o %s %s %s" %
(self.volume_fstype, self.mount_options, self.device_path,
self.mount_point))
child = pexpect.spawn(cmd)
child.expect(pexpect.EOF)
def write_to_fstab(self):
fstab_line = ("%s\t%s\t%s\t%s\t0\t0" %
(self.device_path, self.mount_point, self.volume_fstype,
self.mount_options))
LOG.debug("Writing new line to fstab:%s" % fstab_line)
utils.execute("sudo", "cp", "/etc/fstab", "/etc/fstab.orig")
utils.execute("sudo", "cp", "/etc/fstab", "/tmp/newfstab")
utils.execute("sudo", "chmod", "666", "/tmp/newfstab")
with open("/tmp/newfstab", 'a') as new_fstab:
new_fstab.write("\n" + fstab_line)
utils.execute("sudo", "chmod", "640", "/tmp/newfstab")
utils.execute("sudo", "mv", "/tmp/newfstab", "/etc/fstab")