Support preserve_ephemeral in baremetal.

We need to be able to capture the semantic intent of a rebuild with
ephemeral preservation for the baremetal helper, which means another
column in bm.modes.

Partial-Bug: #1174154
blueprint: baremetal-preserve-ephemeral
Co-Authored-By: Roman Podoliaka <rpodolyaka@mirantis.com>
Change-Id: Icf627f38dac02b7d06ff0f1816741e5513a4fcf3
This commit is contained in:
Robert Collins
2013-12-02 16:21:31 +13:00
committed by Roman Podoliaka
parent 5f5e87af20
commit b0d03ffa0c
6 changed files with 171 additions and 1 deletions

View File

@@ -352,6 +352,7 @@ class BareMetalDeploy(object):
'root_mb': int(d['root_mb']),
'swap_mb': int(d['swap_mb']),
'ephemeral_mb': int(d['ephemeral_mb']),
'preserve_ephemeral': d['preserve_ephemeral'],
}
# Restart worker, if needed
if not self.worker.isAlive():

View File

@@ -2568,6 +2568,29 @@ class TestBaremetalMigrations(BaseWalkMigrationTestCase, CommonTestsMixIn):
def _post_downgrade_008(self, engine):
db_utils.get_table(engine, 'bm_pxe_ips')
def _pre_upgrade_010(self, engine):
bm_nodes = db_utils.get_table(engine, 'bm_nodes')
data = [{'id': 10, 'prov_mac_address': 'cc:cc:cc:cc:cc:cc'}]
engine.execute(bm_nodes.insert(), data)
return data
def _check_010(self, engine, data):
bm_nodes = db_utils.get_table(engine, 'bm_nodes')
self.assertIn('preserve_ephemeral', bm_nodes.columns)
default = engine.execute(
sqlalchemy.select([bm_nodes.c.preserve_ephemeral])
.where(bm_nodes.c.id == data[0]['id'])
).scalar()
self.assertEqual(default, False)
bm_nodes.delete().where(bm_nodes.c.id == data[0]['id']).execute()
def _post_downgrade_010(self, engine):
bm_nodes = db_utils.get_table(engine, 'bm_nodes')
self.assertNotIn('preserve_ephemeral', bm_nodes.columns)
class ProjectTestCase(test.NoDBTestCase):

View File

@@ -24,8 +24,10 @@ import mox
from oslo.config import cfg
from nova.compute import power_state
from nova.compute import task_states
from nova import db as main_db
from nova import exception
from nova.objects import instance as instance_obj
from nova import test
from nova.tests.image import fake as fake_image
from nova.tests import utils
@@ -134,6 +136,23 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase):
block_device_info=result['spawn_params']['block_device_info'],
)
instance = instance_obj.Instance._from_db_object(
self.context, instance_obj.Instance(), result['instance'])
instance.node = result['node']['uuid']
result['rebuild_params'] = dict(
context=self.context,
instance=instance,
image_meta=utils.get_test_image_info(None, result['instance']),
injected_files=[('/fake/path', 'hello world')],
admin_password='test_pass',
bdms={},
detach_block_devices=self.mox.CreateMockAnything(),
attach_block_devices=self.mox.CreateMockAnything(),
network_info=result['spawn_params']['network_info'],
block_device_info=result['spawn_params']['block_device_info'],
)
return result
def test_get_host_stats(self):
@@ -172,6 +191,29 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase):
node['instance']['uuid'])
self.assertEqual(instance['default_ephemeral_device'], None)
def _test_rebuild(self, ephemeral):
node = self._create_node(ephemeral=ephemeral)
self.driver.spawn(**node['spawn_params'])
after_spawn = db.bm_node_get(self.context, node['node']['id'])
instance = node['rebuild_params']['instance']
instance.task_state = task_states.REBUILDING
instance.save(expected_task_state=[None])
self.driver.rebuild(preserve_ephemeral=ephemeral,
**node['rebuild_params'])
after_rebuild = db.bm_node_get(self.context, node['node']['id'])
self.assertEqual(after_rebuild['task_state'], baremetal_states.ACTIVE)
self.assertEqual(after_rebuild['preserve_ephemeral'], ephemeral)
self.assertEqual(after_spawn['instance_uuid'],
after_rebuild['instance_uuid'])
def test_rebuild_ok(self):
self._test_rebuild(ephemeral=False)
def test_rebuild_preserve_ephemeral(self):
self._test_rebuild(ephemeral=True)
def test_macs_from_nic_for_instance(self):
node = self._create_node()
expected = set([nic['address'] for nic in node['nic_info']])

View File

@@ -0,0 +1,51 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# 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 sqlalchemy import Column, MetaData, Boolean, Table, text
COLUMN_NAME = 'preserve_ephemeral'
TABLE_NAME = 'bm_nodes'
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
t = Table(TABLE_NAME, meta, autoload=True)
default = text('0') if migrate_engine.name == 'sqlite' else text('false')
preserve_ephemeral_col = Column(COLUMN_NAME, Boolean,
server_default=default)
t.create_column(preserve_ephemeral_col)
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
t = Table(TABLE_NAME, meta, autoload=True)
# NOTE(rpodolyaka): SQLite doesn't have native BOOLEAN type, so it's
# emulated by adding a CHECK constraint. We must
# explicitly omit that constraint here so we don't
# receive 'no such column' error when dropping the
# column
if migrate_engine.name == 'sqlite':
t.constraints = set([
c
for c in t.constraints
if not (hasattr(c, 'sqltext') and COLUMN_NAME in str(c.sqltext))
])
t.drop_column(COLUMN_NAME)

View File

@@ -42,6 +42,7 @@ class BareMetalNode(BASE, models.NovaBase):
cpus = Column(Integer)
memory_mb = Column(Integer)
local_gb = Column(Integer)
preserve_ephemeral = Column(Boolean)
pm_address = Column(Text)
pm_user = Column(Text)
pm_password = Column(Text)

View File

@@ -25,6 +25,7 @@ from oslo.config import cfg
from nova.compute import flavors
from nova.compute import power_state
from nova.compute import task_states
from nova import context as nova_context
from nova import exception
from nova.openstack.common import excutils
@@ -236,8 +237,14 @@ class BareMetalDriver(driver.ComputeDriver):
node = db.bm_node_associate_and_update(context, node_uuid,
{'instance_uuid': instance['uuid'],
'instance_name': instance['hostname'],
'task_state': baremetal_states.BUILDING})
'task_state': baremetal_states.BUILDING,
'preserve_ephemeral': False})
self._spawn(node, context, instance, image_meta, injected_files,
admin_password, network_info=network_info,
block_device_info=block_device_info)
def _spawn(self, node, context, instance, image_meta, injected_files,
admin_password, network_info=None, block_device_info=None):
try:
self._plug_vifs(instance, network_info, context=context)
self._attach_block_devices(instance, block_device_info)
@@ -283,6 +290,51 @@ class BareMetalDriver(driver.ComputeDriver):
_update_state(context, node, None, baremetal_states.DELETED)
def rebuild(self, context, instance, image_meta, injected_files,
admin_password, bdms, detach_block_devices,
attach_block_devices, network_info=None, recreate=False,
block_device_info=None, preserve_ephemeral=False):
"""Destroy and re-make this instance.
A 'rebuild' effectively purges all existing data from the system and
remakes the VM with given 'metadata' and 'personalities'.
:param context: Security context.
:param instance: Instance object.
:param image_meta: Image object returned by nova.image.glance that
defines the image from which to boot this instance.
:param injected_files: User files to inject into instance.
:param admin_password: Administrator password to set in instance.
:param bdms: block-device-mappings to use for rebuild
:param detach_block_devices: function to detach block devices. See
nova.compute.manager.ComputeManager:_rebuild_default_impl for
usage.
:param attach_block_devices: function to attach block devices. See
nova.compute.manager.ComputeManager:_rebuild_default_impl for
usage.
:param network_info:
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
:param block_device_info: Information about block devices to be
attached to the instance.
:param recreate: True if instance should be recreated with same disk.
:param preserve_ephemeral: True if the default ephemeral storage
partition must be preserved on rebuild.
"""
instance.task_state = task_states.REBUILD_SPAWNING
instance.save(expected_task_state=[task_states.REBUILDING])
node_uuid = self._require_node(instance)
node = db.bm_node_get_by_node_uuid(context, node_uuid)
db.bm_node_update(
context, node['id'],
{'task_state': baremetal_states.BUILDING,
'preserve_ephemeral': preserve_ephemeral}
)
self._spawn(node, context, instance, image_meta, injected_files,
admin_password, network_info=network_info,
block_device_info=block_device_info)
def reboot(self, context, instance, network_info, reboot_type,
block_device_info=None, bad_volumes_callback=None):
node = _get_baremetal_node_by_instance_uuid(instance['uuid'])