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:
committed by
Roman Podoliaka
parent
5f5e87af20
commit
b0d03ffa0c
@@ -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():
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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']])
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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'])
|
||||
|
||||
Reference in New Issue
Block a user