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']),
|
'root_mb': int(d['root_mb']),
|
||||||
'swap_mb': int(d['swap_mb']),
|
'swap_mb': int(d['swap_mb']),
|
||||||
'ephemeral_mb': int(d['ephemeral_mb']),
|
'ephemeral_mb': int(d['ephemeral_mb']),
|
||||||
|
'preserve_ephemeral': d['preserve_ephemeral'],
|
||||||
}
|
}
|
||||||
# Restart worker, if needed
|
# Restart worker, if needed
|
||||||
if not self.worker.isAlive():
|
if not self.worker.isAlive():
|
||||||
|
|||||||
@@ -2568,6 +2568,29 @@ class TestBaremetalMigrations(BaseWalkMigrationTestCase, CommonTestsMixIn):
|
|||||||
def _post_downgrade_008(self, engine):
|
def _post_downgrade_008(self, engine):
|
||||||
db_utils.get_table(engine, 'bm_pxe_ips')
|
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):
|
class ProjectTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ import mox
|
|||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova import db as main_db
|
from nova import db as main_db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
from nova.objects import instance as instance_obj
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.image import fake as fake_image
|
from nova.tests.image import fake as fake_image
|
||||||
from nova.tests import utils
|
from nova.tests import utils
|
||||||
@@ -134,6 +136,23 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase):
|
|||||||
block_device_info=result['spawn_params']['block_device_info'],
|
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
|
return result
|
||||||
|
|
||||||
def test_get_host_stats(self):
|
def test_get_host_stats(self):
|
||||||
@@ -172,6 +191,29 @@ class BareMetalDriverWithDBTestCase(bm_db_base.BMDBTestCase):
|
|||||||
node['instance']['uuid'])
|
node['instance']['uuid'])
|
||||||
self.assertEqual(instance['default_ephemeral_device'], None)
|
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):
|
def test_macs_from_nic_for_instance(self):
|
||||||
node = self._create_node()
|
node = self._create_node()
|
||||||
expected = set([nic['address'] for nic in node['nic_info']])
|
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)
|
cpus = Column(Integer)
|
||||||
memory_mb = Column(Integer)
|
memory_mb = Column(Integer)
|
||||||
local_gb = Column(Integer)
|
local_gb = Column(Integer)
|
||||||
|
preserve_ephemeral = Column(Boolean)
|
||||||
pm_address = Column(Text)
|
pm_address = Column(Text)
|
||||||
pm_user = Column(Text)
|
pm_user = Column(Text)
|
||||||
pm_password = Column(Text)
|
pm_password = Column(Text)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from oslo.config import cfg
|
|||||||
|
|
||||||
from nova.compute import flavors
|
from nova.compute import flavors
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova import context as nova_context
|
from nova import context as nova_context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import excutils
|
from nova.openstack.common import excutils
|
||||||
@@ -236,8 +237,14 @@ class BareMetalDriver(driver.ComputeDriver):
|
|||||||
node = db.bm_node_associate_and_update(context, node_uuid,
|
node = db.bm_node_associate_and_update(context, node_uuid,
|
||||||
{'instance_uuid': instance['uuid'],
|
{'instance_uuid': instance['uuid'],
|
||||||
'instance_name': instance['hostname'],
|
'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:
|
try:
|
||||||
self._plug_vifs(instance, network_info, context=context)
|
self._plug_vifs(instance, network_info, context=context)
|
||||||
self._attach_block_devices(instance, block_device_info)
|
self._attach_block_devices(instance, block_device_info)
|
||||||
@@ -283,6 +290,51 @@ class BareMetalDriver(driver.ComputeDriver):
|
|||||||
|
|
||||||
_update_state(context, node, None, baremetal_states.DELETED)
|
_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,
|
def reboot(self, context, instance, network_info, reboot_type,
|
||||||
block_device_info=None, bad_volumes_callback=None):
|
block_device_info=None, bad_volumes_callback=None):
|
||||||
node = _get_baremetal_node_by_instance_uuid(instance['uuid'])
|
node = _get_baremetal_node_by_instance_uuid(instance['uuid'])
|
||||||
|
|||||||
Reference in New Issue
Block a user