nova-2013.2-r2.ebuild added
This commit is contained in:
parent
da59d247a1
commit
4edffdeb9f
|
@ -0,0 +1,432 @@
|
||||||
|
From 135faa7b5d9855312bedc19e5e1ecebae34d3d18 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <pbrady@redhat.com>
|
||||||
|
Date: Fri, 27 Sep 2013 04:07:14 +0100
|
||||||
|
Subject: [PATCH] ensure we don't boot oversized images
|
||||||
|
|
||||||
|
Since we can't generally shrink incoming images, add extra checks
|
||||||
|
to ensure oversized images are not allowed through.
|
||||||
|
All cases when populating the libvirt image cache are now handled,
|
||||||
|
including the initial download from glance, where we avoid
|
||||||
|
converting to raw, as that could generate non sparse images
|
||||||
|
much larger than the downloaded image.
|
||||||
|
|
||||||
|
* nova/virt/libvirt/utils.py (fetch_image): Allow passing through
|
||||||
|
of the max_size parameter.
|
||||||
|
* nova/virt/images.py (fetch_to_raw): Accept the max_size parameter,
|
||||||
|
and use it to discard images with larger (virtual) sizes.
|
||||||
|
* nova/virt/libvirt/imagebackend.py (verify_base_size): A new
|
||||||
|
refactored function to identify and raise exception to oversized images.
|
||||||
|
(Raw.create_image): Pass the max_size to the fetch function.
|
||||||
|
Also enforce virtual image size checking for already fetched images,
|
||||||
|
as this class (despite the name) can be handling qcow files.
|
||||||
|
(Qcow2.create_image): Pass the max_size to the fetch function,
|
||||||
|
or verify the virtual size for the instance as done previously.
|
||||||
|
(Lvm.create_image): Pass the max_size to the fetch function.
|
||||||
|
Also check the size before transferring to the volume to improve
|
||||||
|
efficiency by not even attempting the transfer of oversized images.
|
||||||
|
(Rbd.create_image): Likewise.
|
||||||
|
* nova/tests/fake_libvirt_utils.py: Support max_size arg.
|
||||||
|
* nova/tests/test_libvirt.py (test_fetch_raw_image):
|
||||||
|
Add a case to check oversized images are discarded.
|
||||||
|
* nova/tests/test_imagebackend.py (test_create_image_too_small):
|
||||||
|
Adjust to avoid the fetch size check.
|
||||||
|
|
||||||
|
Fixes bug: 1177830
|
||||||
|
Fixes bug: 1206081
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
|
||||||
|
nova/tests/test_imagebackend.py
|
||||||
|
nova/virt/libvirt/imagebackend.py
|
||||||
|
|
||||||
|
Change-Id: Idc35fce580be4f74e23883d1b4bea6475c3f6e30
|
||||||
|
---
|
||||||
|
nova/tests/fake_libvirt_utils.py | 2 +-
|
||||||
|
nova/tests/test_imagebackend.py | 35 ++++++++++-------------------
|
||||||
|
nova/tests/test_libvirt.py | 24 +++++++++++++++++---
|
||||||
|
nova/virt/images.py | 24 +++++++++++++++++---
|
||||||
|
nova/virt/libvirt/imagebackend.py | 47 ++++++++++++++++++++++++++++++---------
|
||||||
|
nova/virt/libvirt/utils.py | 5 +++--
|
||||||
|
6 files changed, 95 insertions(+), 42 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py
|
||||||
|
index 23b758e..ecf357a 100644
|
||||||
|
--- a/nova/tests/fake_libvirt_utils.py
|
||||||
|
+++ b/nova/tests/fake_libvirt_utils.py
|
||||||
|
@@ -193,7 +193,7 @@ def get_fs_info(path):
|
||||||
|
'free': 84 * (1024 ** 3)}
|
||||||
|
|
||||||
|
|
||||||
|
-def fetch_image(context, target, image_id, user_id, project_id):
|
||||||
|
+def fetch_image(context, target, image_id, user_id, project_id, max_size=0):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/nova/tests/test_imagebackend.py b/nova/tests/test_imagebackend.py
|
||||||
|
index 77446e8..93ed23d 100644
|
||||||
|
--- a/nova/tests/test_imagebackend.py
|
||||||
|
+++ b/nova/tests/test_imagebackend.py
|
||||||
|
@@ -189,7 +189,7 @@ def prepare_mocks(self):
|
||||||
|
|
||||||
|
def test_create_image(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH, image_id=None)
|
||||||
|
+ fn(target=self.TEMPLATE_PATH, max_size=None, image_id=None)
|
||||||
|
imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, self.PATH)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@@ -210,7 +210,7 @@ def test_create_image_generated(self):
|
||||||
|
|
||||||
|
def test_create_image_extend(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH, image_id=None)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH, image_id=None)
|
||||||
|
imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, self.PATH)
|
||||||
|
imagebackend.disk.extend(self.PATH, self.SIZE)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
@@ -260,7 +260,7 @@ def prepare_mocks(self):
|
||||||
|
|
||||||
|
def test_create_image(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=None, target=self.TEMPLATE_PATH)
|
||||||
|
imagebackend.libvirt_utils.create_cow_image(self.TEMPLATE_PATH,
|
||||||
|
self.PATH)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
@@ -272,15 +272,12 @@ def test_create_image(self):
|
||||||
|
|
||||||
|
def test_create_image_with_size(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.mox.StubOutWithMock(os.path, 'exists')
|
||||||
|
- self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||||
|
if self.OLD_STYLE_INSTANCE_PATH:
|
||||||
|
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
|
||||||
|
os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
|
||||||
|
os.path.exists(self.PATH).AndReturn(False)
|
||||||
|
- imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
- ).AndReturn(self.SIZE)
|
||||||
|
os.path.exists(self.PATH).AndReturn(False)
|
||||||
|
imagebackend.libvirt_utils.create_cow_image(self.TEMPLATE_PATH,
|
||||||
|
self.PATH)
|
||||||
|
@@ -294,27 +291,24 @@ def test_create_image_with_size(self):
|
||||||
|
|
||||||
|
def test_create_image_too_small(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
self.mox.StubOutWithMock(os.path, 'exists')
|
||||||
|
self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||||
|
if self.OLD_STYLE_INSTANCE_PATH:
|
||||||
|
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
|
||||||
|
- os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
|
||||||
|
- os.path.exists(self.PATH).AndReturn(False)
|
||||||
|
+ os.path.exists(self.TEMPLATE_PATH).AndReturn(True)
|
||||||
|
imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
).AndReturn(self.SIZE)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
image = self.image_class(self.INSTANCE, self.NAME)
|
||||||
|
- self.assertRaises(exception.ImageTooLarge, image.create_image, fn,
|
||||||
|
- self.TEMPLATE_PATH, 1)
|
||||||
|
+ self.assertRaises(exception.InstanceTypeDiskTooSmall,
|
||||||
|
+ image.create_image, fn, self.TEMPLATE_PATH, 1)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def test_generate_resized_backing_files(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.mox.StubOutWithMock(os.path, 'exists')
|
||||||
|
- self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||||
|
self.mox.StubOutWithMock(imagebackend.libvirt_utils,
|
||||||
|
'get_disk_backing_file')
|
||||||
|
if self.OLD_STYLE_INSTANCE_PATH:
|
||||||
|
@@ -329,8 +323,6 @@ def test_generate_resized_backing_files(self):
|
||||||
|
self.QCOW2_BASE)
|
||||||
|
imagebackend.disk.extend(self.QCOW2_BASE, self.SIZE)
|
||||||
|
|
||||||
|
- imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
- ).AndReturn(self.SIZE)
|
||||||
|
os.path.exists(self.PATH).AndReturn(True)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@@ -341,9 +333,8 @@ def test_generate_resized_backing_files(self):
|
||||||
|
|
||||||
|
def test_qcow2_exists_and_has_no_backing_file(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.mox.StubOutWithMock(os.path, 'exists')
|
||||||
|
- self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||||
|
self.mox.StubOutWithMock(imagebackend.libvirt_utils,
|
||||||
|
'get_disk_backing_file')
|
||||||
|
if self.OLD_STYLE_INSTANCE_PATH:
|
||||||
|
@@ -353,8 +344,6 @@ def test_qcow2_exists_and_has_no_backing_file(self):
|
||||||
|
|
||||||
|
imagebackend.libvirt_utils.get_disk_backing_file(self.PATH)\
|
||||||
|
.AndReturn(None)
|
||||||
|
- imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
- ).AndReturn(self.SIZE)
|
||||||
|
os.path.exists(self.PATH).AndReturn(True)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@@ -391,7 +380,7 @@ def prepare_mocks(self):
|
||||||
|
|
||||||
|
def _create_image(self, sparse):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=None, target=self.TEMPLATE_PATH)
|
||||||
|
self.libvirt_utils.create_lvm_image(self.VG,
|
||||||
|
self.LV,
|
||||||
|
self.TEMPLATE_SIZE,
|
||||||
|
@@ -423,7 +412,7 @@ def _create_image_generated(self, sparse):
|
||||||
|
|
||||||
|
def _create_image_resize(self, sparse):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.libvirt_utils.create_lvm_image(self.VG, self.LV,
|
||||||
|
self.SIZE, sparse=sparse)
|
||||||
|
self.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
@@ -462,7 +451,7 @@ def test_create_image_resize_sparsed(self):
|
||||||
|
|
||||||
|
def test_create_image_negative(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.libvirt_utils.create_lvm_image(self.VG,
|
||||||
|
self.LV,
|
||||||
|
self.SIZE,
|
||||||
|
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
|
||||||
|
index d8c4cf2..e422ec7 100644
|
||||||
|
--- a/nova/tests/test_libvirt.py
|
||||||
|
+++ b/nova/tests/test_libvirt.py
|
||||||
|
@@ -4826,7 +4826,8 @@ def test_fetch_image(self):
|
||||||
|
image_id = '4'
|
||||||
|
user_id = 'fake'
|
||||||
|
project_id = 'fake'
|
||||||
|
- images.fetch_to_raw(context, image_id, target, user_id, project_id)
|
||||||
|
+ images.fetch_to_raw(context, image_id, target, user_id, project_id,
|
||||||
|
+ max_size=0)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
libvirt_utils.fetch_image(context, target, image_id,
|
||||||
|
@@ -4856,20 +4857,27 @@ class FakeImgInfo(object):
|
||||||
|
file_format = path.split('.')[-2]
|
||||||
|
elif file_format == 'converted':
|
||||||
|
file_format = 'raw'
|
||||||
|
+
|
||||||
|
if 'backing' in path:
|
||||||
|
backing_file = 'backing'
|
||||||
|
else:
|
||||||
|
backing_file = None
|
||||||
|
|
||||||
|
+ if 'big' in path:
|
||||||
|
+ virtual_size = 2
|
||||||
|
+ else:
|
||||||
|
+ virtual_size = 1
|
||||||
|
+
|
||||||
|
FakeImgInfo.file_format = file_format
|
||||||
|
FakeImgInfo.backing_file = backing_file
|
||||||
|
+ FakeImgInfo.virtual_size = virtual_size
|
||||||
|
|
||||||
|
return FakeImgInfo()
|
||||||
|
|
||||||
|
self.stubs.Set(utils, 'execute', fake_execute)
|
||||||
|
self.stubs.Set(os, 'rename', fake_rename)
|
||||||
|
self.stubs.Set(os, 'unlink', fake_unlink)
|
||||||
|
- self.stubs.Set(images, 'fetch', lambda *_: None)
|
||||||
|
+ self.stubs.Set(images, 'fetch', lambda *_, **__: None)
|
||||||
|
self.stubs.Set(images, 'qemu_img_info', fake_qemu_img_info)
|
||||||
|
self.stubs.Set(utils, 'delete_if_exists', fake_rm_on_errror)
|
||||||
|
|
||||||
|
@@ -4884,7 +4892,8 @@ class FakeImgInfo(object):
|
||||||
|
't.qcow2.part', 't.qcow2.converted'),
|
||||||
|
('rm', 't.qcow2.part'),
|
||||||
|
('mv', 't.qcow2.converted', 't.qcow2')]
|
||||||
|
- images.fetch_to_raw(context, image_id, target, user_id, project_id)
|
||||||
|
+ images.fetch_to_raw(context, image_id, target, user_id, project_id,
|
||||||
|
+ max_size=1)
|
||||||
|
self.assertEqual(self.executes, expected_commands)
|
||||||
|
|
||||||
|
target = 't.raw'
|
||||||
|
@@ -4901,6 +4910,15 @@ class FakeImgInfo(object):
|
||||||
|
context, image_id, target, user_id, project_id)
|
||||||
|
self.assertEqual(self.executes, expected_commands)
|
||||||
|
|
||||||
|
+ target = 'big.qcow2'
|
||||||
|
+ self.executes = []
|
||||||
|
+ expected_commands = [('rm', '-f', 'big.qcow2.part')]
|
||||||
|
+ self.assertRaises(exception.InstanceTypeDiskTooSmall,
|
||||||
|
+ images.fetch_to_raw,
|
||||||
|
+ context, image_id, target, user_id, project_id,
|
||||||
|
+ max_size=1)
|
||||||
|
+ self.assertEqual(self.executes, expected_commands)
|
||||||
|
+
|
||||||
|
del self.executes
|
||||||
|
|
||||||
|
def test_get_disk_backing_file(self):
|
||||||
|
diff --git a/nova/virt/images.py b/nova/virt/images.py
|
||||||
|
index b40f566..541779a 100755
|
||||||
|
--- a/nova/virt/images.py
|
||||||
|
+++ b/nova/virt/images.py
|
||||||
|
@@ -190,7 +190,7 @@ def convert_image(source, dest, out_format, run_as_root=False):
|
||||||
|
utils.execute(*cmd, run_as_root=run_as_root)
|
||||||
|
|
||||||
|
|
||||||
|
-def fetch(context, image_href, path, _user_id, _project_id):
|
||||||
|
+def fetch(context, image_href, path, _user_id, _project_id, max_size=0):
|
||||||
|
# TODO(vish): Improve context handling and add owner and auth data
|
||||||
|
# when it is added to glance. Right now there is no
|
||||||
|
# auth checking in glance, so we assume that access was
|
||||||
|
@@ -202,9 +202,10 @@ def fetch(context, image_href, path, _user_id, _project_id):
|
||||||
|
image_service.download(context, image_id, image_file)
|
||||||
|
|
||||||
|
|
||||||
|
-def fetch_to_raw(context, image_href, path, user_id, project_id):
|
||||||
|
+def fetch_to_raw(context, image_href, path, user_id, project_id, max_size=0):
|
||||||
|
path_tmp = "%s.part" % path
|
||||||
|
- fetch(context, image_href, path_tmp, user_id, project_id)
|
||||||
|
+ fetch(context, image_href, path_tmp, user_id, project_id,
|
||||||
|
+ max_size=max_size)
|
||||||
|
|
||||||
|
with utils.remove_path_on_error(path_tmp):
|
||||||
|
data = qemu_img_info(path_tmp)
|
||||||
|
@@ -220,6 +221,23 @@ def fetch_to_raw(context, image_href, path, user_id, project_id):
|
||||||
|
raise exception.ImageUnacceptable(image_id=image_href,
|
||||||
|
reason=_("fmt=%(fmt)s backed by: %(backing_file)s") % locals())
|
||||||
|
|
||||||
|
+ # We can't generally shrink incoming images, so disallow
|
||||||
|
+ # images > size of the flavor we're booting. Checking here avoids
|
||||||
|
+ # an immediate DoS where we convert large qcow images to raw
|
||||||
|
+ # (which may compress well but not be sparse).
|
||||||
|
+ # TODO(p-draigbrady): loop through all flavor sizes, so that
|
||||||
|
+ # we might continue here and not discard the download.
|
||||||
|
+ # If we did that we'd have to do the higher level size checks
|
||||||
|
+ # irrespective of whether the base image was prepared or not.
|
||||||
|
+ disk_size = data.virtual_size
|
||||||
|
+ if max_size and max_size < disk_size:
|
||||||
|
+ msg = _('%(base)s virtual size %(disk_size)s '
|
||||||
|
+ 'larger than flavor root disk size %(size)s')
|
||||||
|
+ LOG.error(msg % {'base': path,
|
||||||
|
+ 'disk_size': disk_size,
|
||||||
|
+ 'size': max_size})
|
||||||
|
+ raise exception.InstanceTypeDiskTooSmall()
|
||||||
|
+
|
||||||
|
if fmt != "raw" and CONF.force_raw_images:
|
||||||
|
staged = "%s.converted" % path
|
||||||
|
LOG.debug("%s was %s, converting to raw" % (image_href, fmt))
|
||||||
|
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
|
||||||
|
index e2c7ccf..dc85c97 100755
|
||||||
|
--- a/nova/virt/libvirt/imagebackend.py
|
||||||
|
+++ b/nova/virt/libvirt/imagebackend.py
|
||||||
|
@@ -177,6 +177,36 @@ def _can_fallocate(self):
|
||||||
|
(CONF.preallocate_images, self.path))
|
||||||
|
return can_fallocate
|
||||||
|
|
||||||
|
+ @staticmethod
|
||||||
|
+ def verify_base_size(base, size, base_size=0):
|
||||||
|
+ """Check that the base image is not larger than size.
|
||||||
|
+ Since images can't be generally shrunk, enforce this
|
||||||
|
+ constraint taking account of virtual image size.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ # Note(pbrady): The size and min_disk parameters of a glance
|
||||||
|
+ # image are checked against the instance size before the image
|
||||||
|
+ # is even downloaded from glance, but currently min_disk is
|
||||||
|
+ # adjustable and doesn't currently account for virtual disk size,
|
||||||
|
+ # so we need this extra check here.
|
||||||
|
+ # NOTE(cfb): Having a flavor that sets the root size to 0 and having
|
||||||
|
+ # nova effectively ignore that size and use the size of the
|
||||||
|
+ # image is considered a feature at this time, not a bug.
|
||||||
|
+
|
||||||
|
+ if size is None:
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ if size and not base_size:
|
||||||
|
+ base_size = disk.get_disk_size(base)
|
||||||
|
+
|
||||||
|
+ if size < base_size:
|
||||||
|
+ msg = _('%(base)s virtual size %(base_size)s '
|
||||||
|
+ 'larger than flavor root disk size %(size)s')
|
||||||
|
+ LOG.error(msg % {'base': base,
|
||||||
|
+ 'base_size': base_size,
|
||||||
|
+ 'size': size})
|
||||||
|
+ raise exception.InstanceTypeDiskTooSmall()
|
||||||
|
+
|
||||||
|
def snapshot_create(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@@ -217,7 +247,8 @@ def copy_raw_image(base, target, size):
|
||||||
|
#Generating image in place
|
||||||
|
prepare_template(target=self.path, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
- prepare_template(target=base, *args, **kwargs)
|
||||||
|
+ prepare_template(target=base, max_size=size, *args, **kwargs)
|
||||||
|
+ self.verify_base_size(base, size)
|
||||||
|
if not os.path.exists(self.path):
|
||||||
|
with utils.remove_path_on_error(self.path):
|
||||||
|
copy_raw_image(base, self.path, size)
|
||||||
|
@@ -257,7 +288,9 @@ def copy_qcow2_image(base, target, size):
|
||||||
|
|
||||||
|
# Download the unmodified base image unless we already have a copy.
|
||||||
|
if not os.path.exists(base):
|
||||||
|
- prepare_template(target=base, *args, **kwargs)
|
||||||
|
+ prepare_template(target=base, max_size=size, *args, **kwargs)
|
||||||
|
+ else:
|
||||||
|
+ self.verify_base_size(base, size)
|
||||||
|
|
||||||
|
legacy_backing_size = None
|
||||||
|
legacy_base = base
|
||||||
|
@@ -283,13 +316,6 @@ def copy_qcow2_image(base, target, size):
|
||||||
|
libvirt_utils.copy_image(base, legacy_base)
|
||||||
|
disk.extend(legacy_base, legacy_backing_size)
|
||||||
|
|
||||||
|
- # NOTE(cfb): Having a flavor that sets the root size to 0 and having
|
||||||
|
- # nova effectively ignore that size and use the size of the
|
||||||
|
- # image is considered a feature at this time, not a bug.
|
||||||
|
- if size and size < disk.get_disk_size(base):
|
||||||
|
- LOG.error('%s virtual size larger than flavor root disk size %s' %
|
||||||
|
- (base, size))
|
||||||
|
- raise exception.ImageTooLarge()
|
||||||
|
if not os.path.exists(self.path):
|
||||||
|
with utils.remove_path_on_error(self.path):
|
||||||
|
copy_qcow2_image(base, self.path, size)
|
||||||
|
@@ -348,6 +374,7 @@ def create_image(self, prepare_template, base, size, *args, **kwargs):
|
||||||
|
lock_path=self.lock_path)
|
||||||
|
def create_lvm_image(base, size):
|
||||||
|
base_size = disk.get_disk_size(base)
|
||||||
|
+ self.verify_base_size(base, size, base_size=base_size)
|
||||||
|
resize = size > base_size
|
||||||
|
size = size if resize else base_size
|
||||||
|
libvirt_utils.create_lvm_image(self.vg, self.lv,
|
||||||
|
@@ -365,7 +392,7 @@ def create_lvm_image(base, size):
|
||||||
|
with self.remove_volume_on_error(self.path):
|
||||||
|
prepare_template(target=self.path, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
- prepare_template(target=base, *args, **kwargs)
|
||||||
|
+ prepare_template(target=base, max_size=size, *args, **kwargs)
|
||||||
|
with self.remove_volume_on_error(self.path):
|
||||||
|
create_lvm_image(base, size)
|
||||||
|
|
||||||
|
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
|
||||||
|
index 6972243..4c31fcb 100755
|
||||||
|
--- a/nova/virt/libvirt/utils.py
|
||||||
|
+++ b/nova/virt/libvirt/utils.py
|
||||||
|
@@ -592,9 +592,10 @@ def get_fs_info(path):
|
||||||
|
'used': used}
|
||||||
|
|
||||||
|
|
||||||
|
-def fetch_image(context, target, image_id, user_id, project_id):
|
||||||
|
+def fetch_image(context, target, image_id, user_id, project_id, max_size=0):
|
||||||
|
"""Grab image."""
|
||||||
|
- images.fetch_to_raw(context, image_id, target, user_id, project_id)
|
||||||
|
+ images.fetch_to_raw(context, image_id, target, user_id, project_id,
|
||||||
|
+ max_size=max_size)
|
||||||
|
|
||||||
|
|
||||||
|
def get_instance_path(instance, forceold=False, relative=False):
|
||||||
|
--
|
||||||
|
1.8.4
|
||||||
|
|
|
@ -0,0 +1,452 @@
|
||||||
|
From 3cdfe894ab58f7b91bf7fb690fc5bc724e44066f Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <pbrady@redhat.com>
|
||||||
|
Date: Fri, 27 Sep 2013 04:07:14 +0100
|
||||||
|
Subject: [PATCH] ensure we don't boot oversized images
|
||||||
|
|
||||||
|
Since we can't generally shrink incoming images, add extra checks
|
||||||
|
to ensure oversized images are not allowed through.
|
||||||
|
All cases when populating the libvirt image cache are now handled,
|
||||||
|
including the initial download from glance, where we avoid
|
||||||
|
converting to raw, as that could generate non sparse images
|
||||||
|
much larger than the downloaded image.
|
||||||
|
|
||||||
|
* nova/virt/libvirt/utils.py (fetch_image): Allow passing through
|
||||||
|
of the max_size parameter.
|
||||||
|
* nova/virt/images.py (fetch_to_raw): Accept the max_size parameter,
|
||||||
|
and use it to discard images with larger (virtual) sizes.
|
||||||
|
* nova/virt/libvirt/imagebackend.py (verify_base_size): A new
|
||||||
|
refactored function to identify and raise exception to oversized images.
|
||||||
|
(Raw.create_image): Pass the max_size to the fetch function.
|
||||||
|
Also enforce virtual image size checking for already fetched images,
|
||||||
|
as this class (despite the name) can be handling qcow files.
|
||||||
|
(Qcow2.create_image): Pass the max_size to the fetch function,
|
||||||
|
or verify the virtual size for the instance as done previously.
|
||||||
|
(Lvm.create_image): Pass the max_size to the fetch function.
|
||||||
|
Also check the size before transferring to the volume to improve
|
||||||
|
efficiency by not even attempting the transfer of oversized images.
|
||||||
|
(Rbd.create_image): Likewise.
|
||||||
|
* nova/tests/virt/libvirt/fake_libvirt_utils.py: Support max_size arg.
|
||||||
|
* nova/tests/virt/libvirt/test_libvirt.py (test_fetch_raw_image):
|
||||||
|
Add a case to check oversized images are discarded.
|
||||||
|
* nova/tests/virt/libvirt/test_imagebackend.py
|
||||||
|
(test_create_image_too_small): Adjust to avoid the fetch size check.
|
||||||
|
|
||||||
|
Fixes bug: 1177830
|
||||||
|
Fixes bug: 1206081
|
||||||
|
Change-Id: I3d47adaa2ad07434853f447feb27d7aae0e2e717
|
||||||
|
---
|
||||||
|
nova/tests/virt/libvirt/fake_libvirt_utils.py | 2 +-
|
||||||
|
nova/tests/virt/libvirt/test_imagebackend.py | 34 ++++++-----------
|
||||||
|
nova/tests/virt/libvirt/test_libvirt.py | 24 ++++++++++--
|
||||||
|
nova/virt/images.py | 24 ++++++++++--
|
||||||
|
nova/virt/libvirt/imagebackend.py | 55 +++++++++++++++++++--------
|
||||||
|
nova/virt/libvirt/utils.py | 5 ++-
|
||||||
|
6 files changed, 98 insertions(+), 46 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/nova/tests/virt/libvirt/fake_libvirt_utils.py b/nova/tests/virt/libvirt/fake_libvirt_utils.py
|
||||||
|
index e18f9df..4799837 100644
|
||||||
|
--- a/nova/tests/virt/libvirt/fake_libvirt_utils.py
|
||||||
|
+++ b/nova/tests/virt/libvirt/fake_libvirt_utils.py
|
||||||
|
@@ -197,7 +197,7 @@ def get_fs_info(path):
|
||||||
|
'free': 84 * (1024 ** 3)}
|
||||||
|
|
||||||
|
|
||||||
|
-def fetch_image(context, target, image_id, user_id, project_id):
|
||||||
|
+def fetch_image(context, target, image_id, user_id, project_id, max_size=0):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/nova/tests/virt/libvirt/test_imagebackend.py b/nova/tests/virt/libvirt/test_imagebackend.py
|
||||||
|
index b862510..2455ec8 100644
|
||||||
|
--- a/nova/tests/virt/libvirt/test_imagebackend.py
|
||||||
|
+++ b/nova/tests/virt/libvirt/test_imagebackend.py
|
||||||
|
@@ -190,7 +190,7 @@ def prepare_mocks(self):
|
||||||
|
|
||||||
|
def test_create_image(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH, image_id=None)
|
||||||
|
+ fn(target=self.TEMPLATE_PATH, max_size=None, image_id=None)
|
||||||
|
imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, self.PATH)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@@ -211,7 +211,7 @@ def test_create_image_generated(self):
|
||||||
|
|
||||||
|
def test_create_image_extend(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH, image_id=None)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH, image_id=None)
|
||||||
|
imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, self.PATH)
|
||||||
|
imagebackend.disk.extend(self.PATH, self.SIZE, use_cow=False)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
@@ -261,7 +261,7 @@ def prepare_mocks(self):
|
||||||
|
|
||||||
|
def test_create_image(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=None, target=self.TEMPLATE_PATH)
|
||||||
|
imagebackend.libvirt_utils.create_cow_image(self.TEMPLATE_PATH,
|
||||||
|
self.PATH)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
@@ -273,15 +273,12 @@ def test_create_image(self):
|
||||||
|
|
||||||
|
def test_create_image_with_size(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.mox.StubOutWithMock(os.path, 'exists')
|
||||||
|
- self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||||
|
if self.OLD_STYLE_INSTANCE_PATH:
|
||||||
|
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
|
||||||
|
os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
|
||||||
|
os.path.exists(self.PATH).AndReturn(False)
|
||||||
|
- imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
- ).AndReturn(self.SIZE)
|
||||||
|
os.path.exists(self.PATH).AndReturn(False)
|
||||||
|
imagebackend.libvirt_utils.create_cow_image(self.TEMPLATE_PATH,
|
||||||
|
self.PATH)
|
||||||
|
@@ -295,13 +292,11 @@ def test_create_image_with_size(self):
|
||||||
|
|
||||||
|
def test_create_image_too_small(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
self.mox.StubOutWithMock(os.path, 'exists')
|
||||||
|
self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||||
|
if self.OLD_STYLE_INSTANCE_PATH:
|
||||||
|
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
|
||||||
|
- os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
|
||||||
|
- os.path.exists(self.PATH).AndReturn(False)
|
||||||
|
+ os.path.exists(self.TEMPLATE_PATH).AndReturn(True)
|
||||||
|
imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
).AndReturn(self.SIZE)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
@@ -313,9 +308,8 @@ def test_create_image_too_small(self):
|
||||||
|
|
||||||
|
def test_generate_resized_backing_files(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.mox.StubOutWithMock(os.path, 'exists')
|
||||||
|
- self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||||
|
self.mox.StubOutWithMock(imagebackend.libvirt_utils,
|
||||||
|
'get_disk_backing_file')
|
||||||
|
if self.OLD_STYLE_INSTANCE_PATH:
|
||||||
|
@@ -330,8 +324,6 @@ def test_generate_resized_backing_files(self):
|
||||||
|
self.QCOW2_BASE)
|
||||||
|
imagebackend.disk.extend(self.QCOW2_BASE, self.SIZE, use_cow=True)
|
||||||
|
|
||||||
|
- imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
- ).AndReturn(self.SIZE)
|
||||||
|
os.path.exists(self.PATH).AndReturn(True)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@@ -342,9 +334,8 @@ def test_generate_resized_backing_files(self):
|
||||||
|
|
||||||
|
def test_qcow2_exists_and_has_no_backing_file(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.mox.StubOutWithMock(os.path, 'exists')
|
||||||
|
- self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||||
|
self.mox.StubOutWithMock(imagebackend.libvirt_utils,
|
||||||
|
'get_disk_backing_file')
|
||||||
|
if self.OLD_STYLE_INSTANCE_PATH:
|
||||||
|
@@ -354,8 +345,6 @@ def test_qcow2_exists_and_has_no_backing_file(self):
|
||||||
|
|
||||||
|
imagebackend.libvirt_utils.get_disk_backing_file(self.PATH)\
|
||||||
|
.AndReturn(None)
|
||||||
|
- imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
- ).AndReturn(self.SIZE)
|
||||||
|
os.path.exists(self.PATH).AndReturn(True)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@@ -392,7 +381,7 @@ def prepare_mocks(self):
|
||||||
|
|
||||||
|
def _create_image(self, sparse):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=None, target=self.TEMPLATE_PATH)
|
||||||
|
self.libvirt_utils.create_lvm_image(self.VG,
|
||||||
|
self.LV,
|
||||||
|
self.TEMPLATE_SIZE,
|
||||||
|
@@ -424,7 +413,7 @@ def _create_image_generated(self, sparse):
|
||||||
|
|
||||||
|
def _create_image_resize(self, sparse):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.libvirt_utils.create_lvm_image(self.VG, self.LV,
|
||||||
|
self.SIZE, sparse=sparse)
|
||||||
|
self.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
|
@@ -463,7 +452,7 @@ def test_create_image_resize_sparsed(self):
|
||||||
|
|
||||||
|
def test_create_image_negative(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
|
self.libvirt_utils.create_lvm_image(self.VG,
|
||||||
|
self.LV,
|
||||||
|
self.SIZE,
|
||||||
|
@@ -607,7 +596,7 @@ def test_cache_template_exists(self):
|
||||||
|
|
||||||
|
def test_create_image(self):
|
||||||
|
fn = self.prepare_mocks()
|
||||||
|
- fn(rbd=self.rbd, target=self.TEMPLATE_PATH)
|
||||||
|
+ fn(max_size=None, rbd=self.rbd, target=self.TEMPLATE_PATH)
|
||||||
|
|
||||||
|
self.rbd.RBD_FEATURE_LAYERING = 1
|
||||||
|
|
||||||
|
@@ -635,6 +624,7 @@ def fake_fetch(target, *args, **kwargs):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.stubs.Set(os.path, 'exists', lambda _: True)
|
||||||
|
+ self.stubs.Set(image, 'check_image_exists', lambda: True)
|
||||||
|
|
||||||
|
image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
|
||||||
|
|
||||||
|
diff --git a/nova/tests/virt/libvirt/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py
|
||||||
|
index ac36be4..5d1361e 100644
|
||||||
|
--- a/nova/tests/virt/libvirt/test_libvirt.py
|
||||||
|
+++ b/nova/tests/virt/libvirt/test_libvirt.py
|
||||||
|
@@ -6308,7 +6308,8 @@ def test_fetch_image(self):
|
||||||
|
image_id = '4'
|
||||||
|
user_id = 'fake'
|
||||||
|
project_id = 'fake'
|
||||||
|
- images.fetch_to_raw(context, image_id, target, user_id, project_id)
|
||||||
|
+ images.fetch_to_raw(context, image_id, target, user_id, project_id,
|
||||||
|
+ max_size=0)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
libvirt_utils.fetch_image(context, target, image_id,
|
||||||
|
@@ -6338,20 +6339,27 @@ class FakeImgInfo(object):
|
||||||
|
file_format = path.split('.')[-2]
|
||||||
|
elif file_format == 'converted':
|
||||||
|
file_format = 'raw'
|
||||||
|
+
|
||||||
|
if 'backing' in path:
|
||||||
|
backing_file = 'backing'
|
||||||
|
else:
|
||||||
|
backing_file = None
|
||||||
|
|
||||||
|
+ if 'big' in path:
|
||||||
|
+ virtual_size = 2
|
||||||
|
+ else:
|
||||||
|
+ virtual_size = 1
|
||||||
|
+
|
||||||
|
FakeImgInfo.file_format = file_format
|
||||||
|
FakeImgInfo.backing_file = backing_file
|
||||||
|
+ FakeImgInfo.virtual_size = virtual_size
|
||||||
|
|
||||||
|
return FakeImgInfo()
|
||||||
|
|
||||||
|
self.stubs.Set(utils, 'execute', fake_execute)
|
||||||
|
self.stubs.Set(os, 'rename', fake_rename)
|
||||||
|
self.stubs.Set(os, 'unlink', fake_unlink)
|
||||||
|
- self.stubs.Set(images, 'fetch', lambda *_: None)
|
||||||
|
+ self.stubs.Set(images, 'fetch', lambda *_, **__: None)
|
||||||
|
self.stubs.Set(images, 'qemu_img_info', fake_qemu_img_info)
|
||||||
|
self.stubs.Set(fileutils, 'delete_if_exists', fake_rm_on_error)
|
||||||
|
|
||||||
|
@@ -6373,7 +6381,8 @@ class FakeImgInfo(object):
|
||||||
|
't.qcow2.part', 't.qcow2.converted'),
|
||||||
|
('rm', 't.qcow2.part'),
|
||||||
|
('mv', 't.qcow2.converted', 't.qcow2')]
|
||||||
|
- images.fetch_to_raw(context, image_id, target, user_id, project_id)
|
||||||
|
+ images.fetch_to_raw(context, image_id, target, user_id, project_id,
|
||||||
|
+ max_size=1)
|
||||||
|
self.assertEqual(self.executes, expected_commands)
|
||||||
|
|
||||||
|
target = 't.raw'
|
||||||
|
@@ -6390,6 +6399,15 @@ class FakeImgInfo(object):
|
||||||
|
context, image_id, target, user_id, project_id)
|
||||||
|
self.assertEqual(self.executes, expected_commands)
|
||||||
|
|
||||||
|
+ target = 'big.qcow2'
|
||||||
|
+ self.executes = []
|
||||||
|
+ expected_commands = [('rm', '-f', 'big.qcow2.part')]
|
||||||
|
+ self.assertRaises(exception.InstanceTypeDiskTooSmall,
|
||||||
|
+ images.fetch_to_raw,
|
||||||
|
+ context, image_id, target, user_id, project_id,
|
||||||
|
+ max_size=1)
|
||||||
|
+ self.assertEqual(self.executes, expected_commands)
|
||||||
|
+
|
||||||
|
del self.executes
|
||||||
|
|
||||||
|
def test_get_disk_backing_file(self):
|
||||||
|
diff --git a/nova/virt/images.py b/nova/virt/images.py
|
||||||
|
index 9c4c101..6d20e65 100644
|
||||||
|
--- a/nova/virt/images.py
|
||||||
|
+++ b/nova/virt/images.py
|
||||||
|
@@ -179,7 +179,7 @@ def convert_image(source, dest, out_format, run_as_root=False):
|
||||||
|
utils.execute(*cmd, run_as_root=run_as_root)
|
||||||
|
|
||||||
|
|
||||||
|
-def fetch(context, image_href, path, _user_id, _project_id):
|
||||||
|
+def fetch(context, image_href, path, _user_id, _project_id, max_size=0):
|
||||||
|
# TODO(vish): Improve context handling and add owner and auth data
|
||||||
|
# when it is added to glance. Right now there is no
|
||||||
|
# auth checking in glance, so we assume that access was
|
||||||
|
@@ -190,9 +190,10 @@ def fetch(context, image_href, path, _user_id, _project_id):
|
||||||
|
image_service.download(context, image_id, dst_path=path)
|
||||||
|
|
||||||
|
|
||||||
|
-def fetch_to_raw(context, image_href, path, user_id, project_id):
|
||||||
|
+def fetch_to_raw(context, image_href, path, user_id, project_id, max_size=0):
|
||||||
|
path_tmp = "%s.part" % path
|
||||||
|
- fetch(context, image_href, path_tmp, user_id, project_id)
|
||||||
|
+ fetch(context, image_href, path_tmp, user_id, project_id,
|
||||||
|
+ max_size=max_size)
|
||||||
|
|
||||||
|
with fileutils.remove_path_on_error(path_tmp):
|
||||||
|
data = qemu_img_info(path_tmp)
|
||||||
|
@@ -209,6 +210,23 @@ def fetch_to_raw(context, image_href, path, user_id, project_id):
|
||||||
|
reason=(_("fmt=%(fmt)s backed by: %(backing_file)s") %
|
||||||
|
{'fmt': fmt, 'backing_file': backing_file}))
|
||||||
|
|
||||||
|
+ # We can't generally shrink incoming images, so disallow
|
||||||
|
+ # images > size of the flavor we're booting. Checking here avoids
|
||||||
|
+ # an immediate DoS where we convert large qcow images to raw
|
||||||
|
+ # (which may compress well but not be sparse).
|
||||||
|
+ # TODO(p-draigbrady): loop through all flavor sizes, so that
|
||||||
|
+ # we might continue here and not discard the download.
|
||||||
|
+ # If we did that we'd have to do the higher level size checks
|
||||||
|
+ # irrespective of whether the base image was prepared or not.
|
||||||
|
+ disk_size = data.virtual_size
|
||||||
|
+ if max_size and max_size < disk_size:
|
||||||
|
+ msg = _('%(base)s virtual size %(disk_size)s '
|
||||||
|
+ 'larger than flavor root disk size %(size)s')
|
||||||
|
+ LOG.error(msg % {'base': path,
|
||||||
|
+ 'disk_size': disk_size,
|
||||||
|
+ 'size': max_size})
|
||||||
|
+ raise exception.InstanceTypeDiskTooSmall()
|
||||||
|
+
|
||||||
|
if fmt != "raw" and CONF.force_raw_images:
|
||||||
|
staged = "%s.converted" % path
|
||||||
|
LOG.debug("%s was %s, converting to raw" % (image_href, fmt))
|
||||||
|
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
|
||||||
|
index 84c46e8..e900789 100644
|
||||||
|
--- a/nova/virt/libvirt/imagebackend.py
|
||||||
|
+++ b/nova/virt/libvirt/imagebackend.py
|
||||||
|
@@ -193,6 +193,36 @@ def _can_fallocate(self):
|
||||||
|
(CONF.preallocate_images, self.path))
|
||||||
|
return can_fallocate
|
||||||
|
|
||||||
|
+ @staticmethod
|
||||||
|
+ def verify_base_size(base, size, base_size=0):
|
||||||
|
+ """Check that the base image is not larger than size.
|
||||||
|
+ Since images can't be generally shrunk, enforce this
|
||||||
|
+ constraint taking account of virtual image size.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ # Note(pbrady): The size and min_disk parameters of a glance
|
||||||
|
+ # image are checked against the instance size before the image
|
||||||
|
+ # is even downloaded from glance, but currently min_disk is
|
||||||
|
+ # adjustable and doesn't currently account for virtual disk size,
|
||||||
|
+ # so we need this extra check here.
|
||||||
|
+ # NOTE(cfb): Having a flavor that sets the root size to 0 and having
|
||||||
|
+ # nova effectively ignore that size and use the size of the
|
||||||
|
+ # image is considered a feature at this time, not a bug.
|
||||||
|
+
|
||||||
|
+ if size is None:
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ if size and not base_size:
|
||||||
|
+ base_size = disk.get_disk_size(base)
|
||||||
|
+
|
||||||
|
+ if size < base_size:
|
||||||
|
+ msg = _('%(base)s virtual size %(base_size)s '
|
||||||
|
+ 'larger than flavor root disk size %(size)s')
|
||||||
|
+ LOG.error(msg % {'base': base,
|
||||||
|
+ 'base_size': base_size,
|
||||||
|
+ 'size': size})
|
||||||
|
+ raise exception.InstanceTypeDiskTooSmall()
|
||||||
|
+
|
||||||
|
def snapshot_create(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@@ -234,7 +264,8 @@ def copy_raw_image(base, target, size):
|
||||||
|
#Generating image in place
|
||||||
|
prepare_template(target=self.path, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
- prepare_template(target=base, *args, **kwargs)
|
||||||
|
+ prepare_template(target=base, max_size=size, *args, **kwargs)
|
||||||
|
+ self.verify_base_size(base, size)
|
||||||
|
if not os.path.exists(self.path):
|
||||||
|
with fileutils.remove_path_on_error(self.path):
|
||||||
|
copy_raw_image(base, self.path, size)
|
||||||
|
@@ -273,7 +304,9 @@ def copy_qcow2_image(base, target, size):
|
||||||
|
|
||||||
|
# Download the unmodified base image unless we already have a copy.
|
||||||
|
if not os.path.exists(base):
|
||||||
|
- prepare_template(target=base, *args, **kwargs)
|
||||||
|
+ prepare_template(target=base, max_size=size, *args, **kwargs)
|
||||||
|
+ else:
|
||||||
|
+ self.verify_base_size(base, size)
|
||||||
|
|
||||||
|
legacy_backing_size = None
|
||||||
|
legacy_base = base
|
||||||
|
@@ -299,17 +332,6 @@ def copy_qcow2_image(base, target, size):
|
||||||
|
libvirt_utils.copy_image(base, legacy_base)
|
||||||
|
disk.extend(legacy_base, legacy_backing_size, use_cow=True)
|
||||||
|
|
||||||
|
- # NOTE(cfb): Having a flavor that sets the root size to 0 and having
|
||||||
|
- # nova effectively ignore that size and use the size of the
|
||||||
|
- # image is considered a feature at this time, not a bug.
|
||||||
|
- disk_size = disk.get_disk_size(base)
|
||||||
|
- if size and size < disk_size:
|
||||||
|
- msg = _('%(base)s virtual size %(disk_size)s'
|
||||||
|
- 'larger than flavor root disk size %(size)s')
|
||||||
|
- LOG.error(msg % {'base': base,
|
||||||
|
- 'disk_size': disk_size,
|
||||||
|
- 'size': size})
|
||||||
|
- raise exception.InstanceTypeDiskTooSmall()
|
||||||
|
if not os.path.exists(self.path):
|
||||||
|
with fileutils.remove_path_on_error(self.path):
|
||||||
|
copy_qcow2_image(base, self.path, size)
|
||||||
|
@@ -367,6 +389,7 @@ def create_image(self, prepare_template, base, size, *args, **kwargs):
|
||||||
|
@utils.synchronized(base, external=True, lock_path=self.lock_path)
|
||||||
|
def create_lvm_image(base, size):
|
||||||
|
base_size = disk.get_disk_size(base)
|
||||||
|
+ self.verify_base_size(base, size, base_size=base_size)
|
||||||
|
resize = size > base_size
|
||||||
|
size = size if resize else base_size
|
||||||
|
libvirt_utils.create_lvm_image(self.vg, self.lv,
|
||||||
|
@@ -384,7 +407,7 @@ def create_lvm_image(base, size):
|
||||||
|
with self.remove_volume_on_error(self.path):
|
||||||
|
prepare_template(target=self.path, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
- prepare_template(target=base, *args, **kwargs)
|
||||||
|
+ prepare_template(target=base, max_size=size, *args, **kwargs)
|
||||||
|
with self.remove_volume_on_error(self.path):
|
||||||
|
create_lvm_image(base, size)
|
||||||
|
|
||||||
|
@@ -514,7 +537,9 @@ def create_image(self, prepare_template, base, size, *args, **kwargs):
|
||||||
|
features = self.rbd.RBD_FEATURE_LAYERING
|
||||||
|
|
||||||
|
if not os.path.exists(base):
|
||||||
|
- prepare_template(target=base, *args, **kwargs)
|
||||||
|
+ prepare_template(target=base, max_size=size, *args, **kwargs)
|
||||||
|
+ else:
|
||||||
|
+ self.verify_base_size(base, size)
|
||||||
|
|
||||||
|
# keep using the command line import instead of librbd since it
|
||||||
|
# detects zeroes to preserve sparseness in the image
|
||||||
|
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
|
||||||
|
index 66ff83e..d7c92b7 100644
|
||||||
|
--- a/nova/virt/libvirt/utils.py
|
||||||
|
+++ b/nova/virt/libvirt/utils.py
|
||||||
|
@@ -639,9 +639,10 @@ def get_fs_info(path):
|
||||||
|
'used': used}
|
||||||
|
|
||||||
|
|
||||||
|
-def fetch_image(context, target, image_id, user_id, project_id):
|
||||||
|
+def fetch_image(context, target, image_id, user_id, project_id, max_size=0):
|
||||||
|
"""Grab image."""
|
||||||
|
- images.fetch_to_raw(context, image_id, target, user_id, project_id)
|
||||||
|
+ images.fetch_to_raw(context, image_id, target, user_id, project_id,
|
||||||
|
+ max_size=max_size)
|
||||||
|
|
||||||
|
|
||||||
|
def get_instance_path(instance, forceold=False, relative=False):
|
||||||
|
--
|
||||||
|
1.8.4
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
From df2ea2e3acdede21b40d47b7adbeac04213d031b Mon Sep 17 00:00:00 2001
|
||||||
|
From: John Garbutt <john.garbutt@rackspace.com>
|
||||||
|
Date: Thu, 12 Sep 2013 18:11:49 +0100
|
||||||
|
Subject: [PATCH] xenapi: enforce filters after live-migration
|
||||||
|
|
||||||
|
Currently and network filters, including security groups, are
|
||||||
|
lost after a server has been live-migrated.
|
||||||
|
|
||||||
|
This partially fixes the issue by ensuring that security groups are
|
||||||
|
re-applied to the VM once it reached the destination, and been started.
|
||||||
|
|
||||||
|
This leaves a small amount of time during the live-migrate where the VM
|
||||||
|
is not protected. There is a further bug raised to close the rest of
|
||||||
|
this whole, but this helps keep the VM protected for the majority of the
|
||||||
|
time.
|
||||||
|
|
||||||
|
Fixes bug 1202266
|
||||||
|
|
||||||
|
(Cherry picked from commit: 5cced7a6dd32d231c606e25dbf762d199bf9cca7)
|
||||||
|
|
||||||
|
Change-Id: I66bc7af1c6da74e18dce47180af0cb6020ba2c1a
|
||||||
|
---
|
||||||
|
nova/tests/test_xenapi.py | 22 +++++++++++++++++++++-
|
||||||
|
nova/virt/xenapi/driver.py | 4 ++--
|
||||||
|
nova/virt/xenapi/vmops.py | 18 ++++++++++++++++++
|
||||||
|
3 files changed, 41 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
|
||||||
|
index f7fb81d..d4c19a4 100644
|
||||||
|
--- a/nova/tests/test_xenapi.py
|
||||||
|
+++ b/nova/tests/test_xenapi.py
|
||||||
|
@@ -2723,7 +2723,27 @@ def test_post_live_migration_at_destination(self):
|
||||||
|
# ensure method is present
|
||||||
|
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
||||||
|
self.conn = xenapi_conn.XenAPIDriver(fake.FakeVirtAPI(), False)
|
||||||
|
- self.conn.post_live_migration_at_destination(None, None, None, None)
|
||||||
|
+
|
||||||
|
+ fake_instance = "instance"
|
||||||
|
+ fake_network_info = "network_info"
|
||||||
|
+
|
||||||
|
+ def fake_fw(instance, network_info):
|
||||||
|
+ self.assertEquals(instance, fake_instance)
|
||||||
|
+ self.assertEquals(network_info, fake_network_info)
|
||||||
|
+ fake_fw.called += 1
|
||||||
|
+
|
||||||
|
+ fake_fw.called = 0
|
||||||
|
+ _vmops = self.conn._vmops
|
||||||
|
+ self.stubs.Set(_vmops.firewall_driver,
|
||||||
|
+ 'setup_basic_filtering', fake_fw)
|
||||||
|
+ self.stubs.Set(_vmops.firewall_driver,
|
||||||
|
+ 'prepare_instance_filter', fake_fw)
|
||||||
|
+ self.stubs.Set(_vmops.firewall_driver,
|
||||||
|
+ 'apply_instance_filter', fake_fw)
|
||||||
|
+
|
||||||
|
+ self.conn.post_live_migration_at_destination(None, fake_instance,
|
||||||
|
+ fake_network_info, None)
|
||||||
|
+ self.assertEqual(fake_fw.called, 3)
|
||||||
|
|
||||||
|
def test_check_can_live_migrate_destination_with_block_migration(self):
|
||||||
|
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
||||||
|
diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py
|
||||||
|
index 128f67f..564c587 100755
|
||||||
|
--- a/nova/virt/xenapi/driver.py
|
||||||
|
+++ b/nova/virt/xenapi/driver.py
|
||||||
|
@@ -1,4 +1,3 @@
|
||||||
|
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright (c) 2010 Citrix Systems, Inc.
|
||||||
|
# Copyright 2010 OpenStack Foundation
|
||||||
|
@@ -514,7 +513,8 @@ def post_live_migration_at_destination(self, ctxt, instance_ref,
|
||||||
|
:params : block_migration: if true, post operation of block_migraiton.
|
||||||
|
"""
|
||||||
|
# TODO(JohnGarbutt) look at moving/downloading ramdisk and kernel
|
||||||
|
- pass
|
||||||
|
+ self._vmops.post_live_migration_at_destination(ctxt, instance_ref,
|
||||||
|
+ network_info, block_device_info, block_device_info)
|
||||||
|
|
||||||
|
def unfilter_instance(self, instance_ref, network_info):
|
||||||
|
"""Removes security groups configured for an instance."""
|
||||||
|
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
|
||||||
|
index eccf3e0..ae5c697 100644
|
||||||
|
--- a/nova/virt/xenapi/vmops.py
|
||||||
|
+++ b/nova/virt/xenapi/vmops.py
|
||||||
|
@@ -1737,6 +1737,24 @@ def live_migrate(self, context, instance, destination_hostname,
|
||||||
|
recover_method(context, instance, destination_hostname,
|
||||||
|
block_migration)
|
||||||
|
|
||||||
|
+ def post_live_migration_at_destination(self, context, instance,
|
||||||
|
+ network_info, block_migration,
|
||||||
|
+ block_device_info):
|
||||||
|
+ # FIXME(johngarbutt): we should block all traffic until we have
|
||||||
|
+ # applied security groups, however this requires changes to XenServer
|
||||||
|
+ try:
|
||||||
|
+ self.firewall_driver.setup_basic_filtering(
|
||||||
|
+ instance, network_info)
|
||||||
|
+ except NotImplementedError:
|
||||||
|
+ # NOTE(salvatore-orlando): setup_basic_filtering might be
|
||||||
|
+ # empty or not implemented at all, as basic filter could
|
||||||
|
+ # be implemented with VIF rules created by xapi plugin
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+ self.firewall_driver.prepare_instance_filter(instance,
|
||||||
|
+ network_info)
|
||||||
|
+ self.firewall_driver.apply_instance_filter(instance, network_info)
|
||||||
|
+
|
||||||
|
def get_per_instance_usage(self):
|
||||||
|
"""Get usage info about each active instance."""
|
||||||
|
usage = {}
|
||||||
|
--
|
||||||
|
1.8.4
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
From 01de658210fd65171bfbf5450c93673b5ce0bd9e Mon Sep 17 00:00:00 2001
|
||||||
|
From: John Garbutt <john.garbutt@rackspace.com>
|
||||||
|
Date: Mon, 21 Oct 2013 19:34:43 +0100
|
||||||
|
Subject: [PATCH] xenapi: apply firewall rules in finish_migrate
|
||||||
|
|
||||||
|
When security groups were added, the rules were not re-applied to
|
||||||
|
servers that have been migrated to a new hypervisor.
|
||||||
|
|
||||||
|
This change ensures the firewall rules are applied as part of creating
|
||||||
|
the new VM in finish_migrate. This code follows a very similar pattern
|
||||||
|
to the code in spawn, and that is where the cut and paste code comes
|
||||||
|
from. This code duplication was removed in Havana.
|
||||||
|
|
||||||
|
Fixes bug 1073306
|
||||||
|
|
||||||
|
Change-Id: I6295a782df328a759e358fb82b76dd3f7bd4b39e
|
||||||
|
---
|
||||||
|
nova/virt/xenapi/vmops.py | 15 +++++++++++++++
|
||||||
|
1 file changed, 15 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
|
||||||
|
index eccf3e0..7a96ac2 100644
|
||||||
|
--- a/nova/virt/xenapi/vmops.py
|
||||||
|
+++ b/nova/virt/xenapi/vmops.py
|
||||||
|
@@ -277,8 +277,23 @@ def finish_migration(self, context, migration, instance, disk_info,
|
||||||
|
|
||||||
|
self._attach_mapped_block_devices(instance, block_device_info)
|
||||||
|
|
||||||
|
+ try:
|
||||||
|
+ self.firewall_driver.setup_basic_filtering(
|
||||||
|
+ instance, network_info)
|
||||||
|
+ except NotImplementedError:
|
||||||
|
+ # NOTE(salvatore-orlando): setup_basic_filtering might be
|
||||||
|
+ # empty or not implemented at all, as basic filter could
|
||||||
|
+ # be implemented with VIF rules created by xapi plugin
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+ self.firewall_driver.prepare_instance_filter(instance,
|
||||||
|
+ network_info)
|
||||||
|
+
|
||||||
|
# 5. Start VM
|
||||||
|
self._start(instance, vm_ref=vm_ref)
|
||||||
|
+
|
||||||
|
+ self.firewall_driver.apply_instance_filter(instance, network_info)
|
||||||
|
+
|
||||||
|
self._update_instance_progress(context, instance,
|
||||||
|
step=5,
|
||||||
|
total_steps=RESIZE_TOTAL_STEPS)
|
||||||
|
--
|
||||||
|
1.8.4
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Copyright 1999-2013 Gentoo Foundation
|
||||||
|
# Distributed under the terms of the GNU General Public License v2
|
||||||
|
# $Header: /var/cvsroot/gentoo-x86/sys-cluster/nova/nova-2013.2-r2.ebuild,v 1.1 2013/11/17 22:35:55 prometheanfire Exp $
|
||||||
|
|
||||||
|
EAPI=5
|
||||||
|
PYTHON_COMPAT=( python2_7 )
|
||||||
|
|
||||||
|
inherit distutils-r1 eutils multilib
|
||||||
|
|
||||||
|
DESCRIPTION="Nova is a cloud computing fabric controller (main part of an
|
||||||
|
IaaS system). It is written in Python."
|
||||||
|
HOMEPAGE="https://launchpad.net/nova"
|
||||||
|
SRC_URI="http://launchpad.net/${PN}/havana/${PV}/+download/${P}.tar.gz"
|
||||||
|
|
||||||
|
LICENSE="Apache-2.0"
|
||||||
|
SLOT="0"
|
||||||
|
KEYWORDS="~amd64 ~x86"
|
||||||
|
IUSE="+api +cert +compute +conductor +consoleauth +kvm +network +novncproxy +scheduler +spicehtml5proxy +xvpvncproxy sqlite mysql postgres xen"
|
||||||
|
REQUIRED_USE="|| ( mysql postgres sqlite )
|
||||||
|
|| ( kvm xen )"
|
||||||
|
|
||||||
|
DEPEND="dev-python/setuptools[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/pbr-0.5.21[${PYTHON_USEDEP}]
|
||||||
|
<dev-python/pbr-1.0[${PYTHON_USEDEP}]
|
||||||
|
app-admin/sudo"
|
||||||
|
|
||||||
|
RDEPEND="sqlite? ( >=dev-python/sqlalchemy-0.7.8[sqlite,${PYTHON_USEDEP}]
|
||||||
|
<dev-python/sqlalchemy-0.7.99[sqlite,${PYTHON_USEDEP}] )
|
||||||
|
mysql? ( >=dev-python/sqlalchemy-0.7.8[mysql,${PYTHON_USEDEP}]
|
||||||
|
<dev-python/sqlalchemy-0.7.99[mysql,${PYTHON_USEDEP}] )
|
||||||
|
postgres? ( >=dev-python/sqlalchemy-0.7.8[postgres,${PYTHON_USEDEP}]
|
||||||
|
<dev-python/sqlalchemy-0.7.99[postgres,${PYTHON_USEDEP}] )
|
||||||
|
>=dev-python/amqplib-0.6.1[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/anyjson-0.3.3[${PYTHON_USEDEP}]
|
||||||
|
virtual/python-argparse[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/boto-2.4.0[${PYTHON_USEDEP}]
|
||||||
|
!~dev-python/boto-2.13.0[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/eventlet-0.13.0[${PYTHON_USEDEP}]
|
||||||
|
dev-python/jinja[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/kombu-2.4.8[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/lxml-2.3[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/routes-1.12.3-r1[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/webob-1.2.3[${PYTHON_USEDEP}]
|
||||||
|
<dev-python/webob-1.3[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/greenlet-0.3.2[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/pastedeploy-1.5.0-r1[${PYTHON_USEDEP}]
|
||||||
|
dev-python/paste[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/sqlalchemy-migrate-0.7.2[${PYTHON_USEDEP}]
|
||||||
|
dev-python/netaddr[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/suds-0.4[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/paramiko-1.8.0[${PYTHON_USEDEP}]
|
||||||
|
dev-python/pyasn1[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/Babel-0.9.6[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/iso8601-0.1.4[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/python-cinderclient-1.0.5[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/python-neutronclient-2.3.0[${PYTHON_USEDEP}]
|
||||||
|
<=dev-python/python-neutronclient-3.0.0[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/python-glanceclient-0.9.0[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/python-keystoneclient-0.3.2[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/stevedore-0.10[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/websockify-0.5.1[${PYTHON_USEDEP}]
|
||||||
|
<dev-python/websockify-0.6[${PYTHON_USEDEP}]
|
||||||
|
>=dev-python/oslo-config-1.2.0[${PYTHON_USEDEP}]
|
||||||
|
app-emulation/libvirt
|
||||||
|
novncproxy? ( www-apps/novnc )
|
||||||
|
sys-apps/iproute2
|
||||||
|
net-misc/openvswitch
|
||||||
|
sys-fs/sysfsutils
|
||||||
|
sys-fs/multipath-tools
|
||||||
|
kvm? ( app-emulation/qemu )
|
||||||
|
xen? ( app-emulation/xen
|
||||||
|
app-emulation/xen-tools )"
|
||||||
|
|
||||||
|
PATCHES=(
|
||||||
|
"${FILESDIR}/CVE-2013-4463_4469-havana.patch"
|
||||||
|
)
|
||||||
|
|
||||||
|
pkg_setup() {
|
||||||
|
enewgroup nova
|
||||||
|
enewuser nova -1 -1 /var/lib/nova nova
|
||||||
|
}
|
||||||
|
|
||||||
|
python_install() {
|
||||||
|
distutils-r1_python_install
|
||||||
|
newconfd "${FILESDIR}/nova-confd" "nova"
|
||||||
|
newinitd "${FILESDIR}/nova-initd" "nova"
|
||||||
|
use api && dosym /etc/init.d/nova /etc/init.d/nova-api
|
||||||
|
use cert && dosym /etc/init.d/nova /etc/init.d/nova-cert
|
||||||
|
use compute && dosym /etc/init.d/nova /etc/init.d/nova-compute
|
||||||
|
use conductor && dosym /etc/init.d/nova /etc/init.d/nova-conductor
|
||||||
|
use consoleauth && dosym /etc/init.d/nova /etc/init.d/nova-consoleauth
|
||||||
|
use network && dosym /etc/init.d/nova /etc/init.d/nova-network
|
||||||
|
use novncproxy &&dosym /etc/init.d/nova /etc/init.d/nova-novncproxy
|
||||||
|
use scheduler && dosym /etc/init.d/nova /etc/init.d/nova-scheduler
|
||||||
|
use spicehtml5proxy && dosym /etc/init.d/nova /etc/init.d/nova-spicehtml5proxy
|
||||||
|
use xvpvncproxy && dosym /etc/init.d/nova /etc/init.d/nova-xvpncproxy
|
||||||
|
|
||||||
|
diropts -m 0750
|
||||||
|
dodir /var/run/nova /var/log/nova /var/lock/nova
|
||||||
|
fowners nova:nova /var/log/nova /var/lock/nova /var/run/nova
|
||||||
|
|
||||||
|
diropts -m 0755
|
||||||
|
dodir /var/lib/nova/instances
|
||||||
|
fowners nova:nova /var/lib/nova/instances
|
||||||
|
|
||||||
|
keepdir /etc/nova
|
||||||
|
insinto /etc/nova
|
||||||
|
newins "etc/nova/nova.conf.sample" "nova.conf"
|
||||||
|
doins "etc/nova/api-paste.ini"
|
||||||
|
doins "etc/nova/logging_sample.conf"
|
||||||
|
doins "etc/nova/policy.json"
|
||||||
|
doins "etc/nova/rootwrap.conf"
|
||||||
|
insinto /etc/nova/rootwrap.d
|
||||||
|
doins "etc/nova/rootwrap.d/api-metadata.filters"
|
||||||
|
doins "etc/nova/rootwrap.d/compute.filters"
|
||||||
|
doins "etc/nova/rootwrap.d/network.filters"
|
||||||
|
|
||||||
|
#copy migration conf file (not coppied on install via setup.py script)
|
||||||
|
insinto /usr/$(get_libdir)/python2.7/site-packages/nova/db/sqlalchemy/migrate_repo/
|
||||||
|
doins "nova/db/sqlalchemy/migrate_repo/migrate.cfg"
|
||||||
|
|
||||||
|
#copy the CA cert dir (not coppied on install via setup.py script)
|
||||||
|
cp -R "${S}/nova/CA" "${D}/usr/$(get_libdir)/python2.7/site-packages/nova/" || die "isntalling CA files failed"
|
||||||
|
|
||||||
|
#add sudoers definitions for user nova
|
||||||
|
insinto /etc/sudoers.d/
|
||||||
|
doins "${FILESDIR}/nova-sudoers"
|
||||||
|
}
|
Loading…
Reference in New Issue