|
Packit Service |
a04d08 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from cloudinit import cloud
|
|
Packit Service |
a04d08 |
from cloudinit.config import cc_growpart
|
|
Packit Service |
9bfd13 |
from cloudinit import subp
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from cloudinit.tests.helpers import TestCase
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
import errno
|
|
Packit Service |
a04d08 |
import logging
|
|
Packit Service |
a04d08 |
import os
|
|
Packit Service |
a04d08 |
import re
|
|
Packit Service |
a04d08 |
import unittest
|
|
Packit Service |
9bfd13 |
from contextlib import ExitStack
|
|
Packit Service |
9bfd13 |
from unittest import mock
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# growpart:
|
|
Packit Service |
a04d08 |
# mode: auto # off, on, auto, 'growpart'
|
|
Packit Service |
a04d08 |
# devices: ['root']
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
HELP_GROWPART_RESIZE = """
|
|
Packit Service |
a04d08 |
growpart disk partition
|
|
Packit Service |
a04d08 |
rewrite partition table so that partition takes up all the space it can
|
|
Packit Service |
a04d08 |
options:
|
|
Packit Service |
a04d08 |
-h | --help print Usage and exit
|
|
Packit Service |
a04d08 |
<SNIP>
|
|
Packit Service |
a04d08 |
-u | --update R update the the kernel partition table info after growing
|
|
Packit Service |
a04d08 |
this requires kernel support and 'partx --update'
|
|
Packit Service |
a04d08 |
R is one of:
|
|
Packit Service |
a04d08 |
- 'auto' : [default] update partition if possible
|
|
Packit Service |
a04d08 |
<SNIP>
|
|
Packit Service |
a04d08 |
Example:
|
|
Packit Service |
a04d08 |
- growpart /dev/sda 1
|
|
Packit Service |
a04d08 |
Resize partition 1 on /dev/sda
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
HELP_GROWPART_NO_RESIZE = """
|
|
Packit Service |
a04d08 |
growpart disk partition
|
|
Packit Service |
a04d08 |
rewrite partition table so that partition takes up all the space it can
|
|
Packit Service |
a04d08 |
options:
|
|
Packit Service |
a04d08 |
-h | --help print Usage and exit
|
|
Packit Service |
a04d08 |
<SNIP>
|
|
Packit Service |
a04d08 |
Example:
|
|
Packit Service |
a04d08 |
- growpart /dev/sda 1
|
|
Packit Service |
a04d08 |
Resize partition 1 on /dev/sda
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
9bfd13 |
HELP_GPART = """
|
|
Packit Service |
9bfd13 |
usage: gpart add -t type [-a alignment] [-b start] <SNIP> geom
|
|
Packit Service |
9bfd13 |
gpart backup geom
|
|
Packit Service |
9bfd13 |
gpart bootcode [-b bootcode] [-p partcode -i index] [-f flags] geom
|
|
Packit Service |
9bfd13 |
<SNIP>
|
|
Packit Service |
9bfd13 |
gpart resize -i index [-a alignment] [-s size] [-f flags] geom
|
|
Packit Service |
9bfd13 |
gpart restore [-lF] [-f flags] provider [...]
|
|
Packit Service |
9bfd13 |
gpart recover [-f flags] geom
|
|
Packit Service |
9bfd13 |
gpart help
|
|
Packit Service |
9bfd13 |
<SNIP>
|
|
Packit Service |
9bfd13 |
"""
|
|
Packit Service |
9bfd13 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class TestDisabled(unittest.TestCase):
|
|
Packit Service |
a04d08 |
def setUp(self):
|
|
Packit Service |
a04d08 |
super(TestDisabled, self).setUp()
|
|
Packit Service |
a04d08 |
self.name = "growpart"
|
|
Packit Service |
a04d08 |
self.cloud_init = None
|
|
Packit Service |
a04d08 |
self.log = logging.getLogger("TestDisabled")
|
|
Packit Service |
a04d08 |
self.args = []
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self.handle = cc_growpart.handle
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_mode_off(self):
|
|
Packit Service |
a04d08 |
# Test that nothing is done if mode is off.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# this really only verifies that resizer_factory isn't called
|
|
Packit Service |
a04d08 |
config = {'growpart': {'mode': 'off'}}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
with mock.patch.object(cc_growpart, 'resizer_factory') as mockobj:
|
|
Packit Service |
a04d08 |
self.handle(self.name, config, self.cloud_init, self.log,
|
|
Packit Service |
a04d08 |
self.args)
|
|
Packit Service |
a04d08 |
self.assertEqual(mockobj.call_count, 0)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class TestConfig(TestCase):
|
|
Packit Service |
a04d08 |
def setUp(self):
|
|
Packit Service |
a04d08 |
super(TestConfig, self).setUp()
|
|
Packit Service |
a04d08 |
self.name = "growpart"
|
|
Packit Service |
a04d08 |
self.paths = None
|
|
Packit Service |
a04d08 |
self.cloud = cloud.Cloud(None, self.paths, None, None, None)
|
|
Packit Service |
a04d08 |
self.log = logging.getLogger("TestConfig")
|
|
Packit Service |
a04d08 |
self.args = []
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self.cloud_init = None
|
|
Packit Service |
a04d08 |
self.handle = cc_growpart.handle
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@mock.patch.dict("os.environ", clear=True)
|
|
Packit Service |
a04d08 |
def test_no_resizers_auto_is_fine(self):
|
|
Packit Service |
a04d08 |
with mock.patch.object(
|
|
Packit Service |
9bfd13 |
subp, 'subp',
|
|
Packit Service |
a04d08 |
return_value=(HELP_GROWPART_NO_RESIZE, "")) as mockobj:
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
config = {'growpart': {'mode': 'auto'}}
|
|
Packit Service |
a04d08 |
self.handle(self.name, config, self.cloud_init, self.log,
|
|
Packit Service |
a04d08 |
self.args)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
9bfd13 |
mockobj.assert_has_calls([
|
|
Packit Service |
9bfd13 |
mock.call(['growpart', '--help'], env={'LANG': 'C'}),
|
|
Packit Service |
9bfd13 |
mock.call(['gpart', 'help'], env={'LANG': 'C'}, rcs=[0, 1])])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@mock.patch.dict("os.environ", clear=True)
|
|
Packit Service |
a04d08 |
def test_no_resizers_mode_growpart_is_exception(self):
|
|
Packit Service |
a04d08 |
with mock.patch.object(
|
|
Packit Service |
9bfd13 |
subp, 'subp',
|
|
Packit Service |
a04d08 |
return_value=(HELP_GROWPART_NO_RESIZE, "")) as mockobj:
|
|
Packit Service |
a04d08 |
config = {'growpart': {'mode': "growpart"}}
|
|
Packit Service |
a04d08 |
self.assertRaises(
|
|
Packit Service |
a04d08 |
ValueError, self.handle, self.name, config,
|
|
Packit Service |
a04d08 |
self.cloud_init, self.log, self.args)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
mockobj.assert_called_once_with(
|
|
Packit Service |
a04d08 |
['growpart', '--help'], env={'LANG': 'C'})
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@mock.patch.dict("os.environ", clear=True)
|
|
Packit Service |
a04d08 |
def test_mode_auto_prefers_growpart(self):
|
|
Packit Service |
a04d08 |
with mock.patch.object(
|
|
Packit Service |
9bfd13 |
subp, 'subp',
|
|
Packit Service |
a04d08 |
return_value=(HELP_GROWPART_RESIZE, "")) as mockobj:
|
|
Packit Service |
a04d08 |
ret = cc_growpart.resizer_factory(mode="auto")
|
|
Packit Service |
a04d08 |
self.assertIsInstance(ret, cc_growpart.ResizeGrowPart)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
mockobj.assert_called_once_with(
|
|
Packit Service |
a04d08 |
['growpart', '--help'], env={'LANG': 'C'})
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
9bfd13 |
@mock.patch.dict("os.environ", clear=True)
|
|
Packit Service |
9bfd13 |
def test_mode_auto_falls_back_to_gpart(self):
|
|
Packit Service |
9bfd13 |
with mock.patch.object(
|
|
Packit Service |
9bfd13 |
subp, 'subp',
|
|
Packit Service |
9bfd13 |
return_value=("", HELP_GPART)) as mockobj:
|
|
Packit Service |
9bfd13 |
ret = cc_growpart.resizer_factory(mode="auto")
|
|
Packit Service |
9bfd13 |
self.assertIsInstance(ret, cc_growpart.ResizeGpart)
|
|
Packit Service |
9bfd13 |
|
|
Packit Service |
9bfd13 |
mockobj.assert_has_calls([
|
|
Packit Service |
9bfd13 |
mock.call(['growpart', '--help'], env={'LANG': 'C'}),
|
|
Packit Service |
9bfd13 |
mock.call(['gpart', 'help'], env={'LANG': 'C'}, rcs=[0, 1])])
|
|
Packit Service |
9bfd13 |
|
|
Packit Service |
a04d08 |
def test_handle_with_no_growpart_entry(self):
|
|
Packit Service |
a04d08 |
# if no 'growpart' entry in config, then mode=auto should be used
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
myresizer = object()
|
|
Packit Service |
a04d08 |
retval = (("/", cc_growpart.RESIZE.CHANGED, "my-message",),)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
with ExitStack() as mocks:
|
|
Packit Service |
a04d08 |
factory = mocks.enter_context(
|
|
Packit Service |
a04d08 |
mock.patch.object(cc_growpart, 'resizer_factory',
|
|
Packit Service |
a04d08 |
return_value=myresizer))
|
|
Packit Service |
a04d08 |
rsdevs = mocks.enter_context(
|
|
Packit Service |
a04d08 |
mock.patch.object(cc_growpart, 'resize_devices',
|
|
Packit Service |
a04d08 |
return_value=retval))
|
|
Packit Service |
a04d08 |
mocks.enter_context(
|
|
Packit Service |
a04d08 |
mock.patch.object(cc_growpart, 'RESIZERS',
|
|
Packit Service |
a04d08 |
(('mysizer', object),)
|
|
Packit Service |
a04d08 |
))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self.handle(self.name, {}, self.cloud_init, self.log, self.args)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
factory.assert_called_once_with('auto')
|
|
Packit Service |
a04d08 |
rsdevs.assert_called_once_with(myresizer, ['/'])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class TestResize(unittest.TestCase):
|
|
Packit Service |
a04d08 |
def setUp(self):
|
|
Packit Service |
a04d08 |
super(TestResize, self).setUp()
|
|
Packit Service |
a04d08 |
self.name = "growpart"
|
|
Packit Service |
a04d08 |
self.log = logging.getLogger("TestResize")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_simple_devices(self):
|
|
Packit Service |
a04d08 |
# test simple device list
|
|
Packit Service |
a04d08 |
# this patches out devent2dev, os.stat, and device_part_info
|
|
Packit Service |
a04d08 |
# so in the end, doesn't test a lot
|
|
Packit Service |
a04d08 |
devs = ["/dev/XXda1", "/dev/YYda2"]
|
|
Packit Service |
a04d08 |
devstat_ret = Bunch(st_mode=25008, st_ino=6078, st_dev=5,
|
|
Packit Service |
a04d08 |
st_nlink=1, st_uid=0, st_gid=6, st_size=0,
|
|
Packit Service |
a04d08 |
st_atime=0, st_mtime=0, st_ctime=0)
|
|
Packit Service |
a04d08 |
enoent = ["/dev/NOENT"]
|
|
Packit Service |
a04d08 |
real_stat = os.stat
|
|
Packit Service |
a04d08 |
resize_calls = []
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class myresizer(object):
|
|
Packit Service |
a04d08 |
def resize(self, diskdev, partnum, partdev):
|
|
Packit Service |
a04d08 |
resize_calls.append((diskdev, partnum, partdev))
|
|
Packit Service |
a04d08 |
if partdev == "/dev/YYda2":
|
|
Packit Service |
a04d08 |
return (1024, 2048)
|
|
Packit Service |
a04d08 |
return (1024, 1024) # old size, new size
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def mystat(path):
|
|
Packit Service |
a04d08 |
if path in devs:
|
|
Packit Service |
a04d08 |
return devstat_ret
|
|
Packit Service |
a04d08 |
if path in enoent:
|
|
Packit Service |
a04d08 |
e = OSError("%s: does not exist" % path)
|
|
Packit Service |
a04d08 |
e.errno = errno.ENOENT
|
|
Packit Service |
a04d08 |
raise e
|
|
Packit Service |
a04d08 |
return real_stat(path)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
opinfo = cc_growpart.device_part_info
|
|
Packit Service |
a04d08 |
cc_growpart.device_part_info = simple_device_part_info
|
|
Packit Service |
a04d08 |
os.stat = mystat
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
resized = cc_growpart.resize_devices(myresizer(), devs + enoent)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def find(name, res):
|
|
Packit Service |
a04d08 |
for f in res:
|
|
Packit Service |
a04d08 |
if f[0] == name:
|
|
Packit Service |
a04d08 |
return f
|
|
Packit Service |
a04d08 |
return None
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self.assertEqual(cc_growpart.RESIZE.NOCHANGE,
|
|
Packit Service |
a04d08 |
find("/dev/XXda1", resized)[1])
|
|
Packit Service |
a04d08 |
self.assertEqual(cc_growpart.RESIZE.CHANGED,
|
|
Packit Service |
a04d08 |
find("/dev/YYda2", resized)[1])
|
|
Packit Service |
a04d08 |
self.assertEqual(cc_growpart.RESIZE.SKIPPED,
|
|
Packit Service |
a04d08 |
find(enoent[0], resized)[1])
|
|
Packit Service |
a04d08 |
# self.assertEqual(resize_calls,
|
|
Packit Service |
a04d08 |
# [("/dev/XXda", "1", "/dev/XXda1"),
|
|
Packit Service |
a04d08 |
# ("/dev/YYda", "2", "/dev/YYda2")])
|
|
Packit Service |
a04d08 |
finally:
|
|
Packit Service |
a04d08 |
cc_growpart.device_part_info = opinfo
|
|
Packit Service |
a04d08 |
os.stat = real_stat
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def simple_device_part_info(devpath):
|
|
Packit Service |
a04d08 |
# simple stupid return (/dev/vda, 1) for /dev/vda
|
|
Packit Service |
a04d08 |
ret = re.search("([^0-9]*)([0-9]*)$", devpath)
|
|
Packit Service |
a04d08 |
x = (ret.group(1), ret.group(2))
|
|
Packit Service |
a04d08 |
return x
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class Bunch(object):
|
|
Packit Service |
a04d08 |
def __init__(self, **kwds):
|
|
Packit Service |
a04d08 |
self.__dict__.update(kwds)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# vi: ts=4 expandtab
|