--- a/components/openstack/horizon/files/overrides.py Tue Feb 09 16:26:27 2016 -0800
+++ b/components/openstack/horizon/files/overrides.py Tue Feb 09 17:07:37 2016 -0800
@@ -85,8 +85,8 @@
project_tables.TerminateInstance
)
-# Remove 'EditInstanceSecurityGroups', 'TogglePause', 'RebuildInstance'
-# actions from Project/Instances/Actions
+# Remove 'EditInstanceSecurityGroups', 'TogglePause' actions from
+# Project/Instances/Actions
project_tables.InstancesTable._meta.row_actions = (
project_tables.StartInstance,
project_tables.ConfirmResize,
@@ -102,6 +102,7 @@
project_tables.SoftRebootInstance,
project_tables.RebootInstance,
project_tables.StopInstance,
+ project_tables.RebuildInstance,
project_tables.TerminateInstance
)
--- a/components/openstack/nova/files/solariszones/driver.py Tue Feb 09 16:26:27 2016 -0800
+++ b/components/openstack/nova/files/solariszones/driver.py Tue Feb 09 17:07:37 2016 -0800
@@ -40,6 +40,7 @@
from eventlet import greenthread
from lxml import etree
from oslo_config import cfg
+from passlib.hash import sha256_crypt
from nova.compute import power_state
from nova.compute import task_states
@@ -148,6 +149,8 @@
ROOTZPOOL_RESOURCE = 'rootzpool'
+shared_storage = ['iscsi', 'fibre_channel']
+
def lookup_resource_property(zone, resource, prop, filter=None):
"""Lookup specified property from specified Solaris Zone resource."""
try:
@@ -429,7 +432,7 @@
capabilities = {
"has_imagecache": False,
- "supports_recreate": False,
+ "supports_recreate": True,
}
def __init__(self, virtapi):
@@ -789,6 +792,28 @@
"""
raise NotImplementedError()
+ def _rebuild_block_devices(self, context, instance, bdms, recreate):
+ root_ci = None
+ rootmp = instance['root_device_name']
+ for entry in bdms:
+ if entry['connection_info'] is None:
+ continue
+
+ if entry['device_name'] == rootmp:
+ root_ci = jsonutils.loads(entry['connection_info'])
+ continue
+
+ if not recreate:
+ ci = jsonutils.loads(entry['connection_info'])
+ self.detach_volume(ci, instance, entry['device_name'])
+
+ if root_ci is None and recreate:
+ msg = (_("Unable to find the root device for instance '%s'.")
+ % instance['name'])
+ raise exception.NovaException(msg)
+
+ return root_ci
+
def rebuild(self, context, instance, image_meta, injected_files,
admin_password, bdms, detach_block_devices,
attach_block_devices, network_info=None,
@@ -829,7 +854,109 @@
:param preserve_ephemeral: True if the default ephemeral storage
partition must be preserved on rebuild
"""
- raise NotImplementedError()
+ if recreate:
+ instance.system_metadata['evac_from'] = instance['launched_on']
+ instance.save()
+ inst_type = flavor_obj.Flavor.get_by_id(
+ nova_context.get_admin_context(read_deleted='yes'),
+ instance['instance_type_id'])
+ extra_specs = inst_type['extra_specs'].copy()
+ brand = extra_specs.get('zonecfg:brand', ZONE_BRAND_SOLARIS)
+ if brand == ZONE_BRAND_SOLARIS:
+ msg = (_("'%s' branded zones do not currently support "
+ "evacuation.") % brand)
+ raise exception.NovaException(msg)
+
+ instance.task_state = task_states.REBUILD_BLOCK_DEVICE_MAPPING
+ instance.save(expected_task_state=[task_states.REBUILDING])
+
+ root_ci = self._rebuild_block_devices(context, instance, bdms,
+ recreate)
+
+ if root_ci is not None:
+ driver_type = root_ci['driver_volume_type']
+ else:
+ driver_type = 'local'
+
+ # If image_meta is provided then the --on-shared-storage option
+ # was not used.
+ if image_meta:
+ # If not then raise an exception. But if this is a rebuild then
+ # the local storage is ok.
+ if driver_type in shared_storage and recreate:
+ msg = (_("Root device is on shared storage for instance '%s'.")
+ % instance['name'])
+ raise exception.NovaException(msg)
+
+ else:
+ # So the root device is not expected to be local so we can move
+ # forward with building the zone.
+ if driver_type not in shared_storage:
+ msg = (_("Root device is not on shared storage for instance "
+ "'%s'.") % instance['name'])
+ raise exception.NovaException(msg)
+
+ if not recreate:
+ self.destroy(context, instance, network_info, block_device_info)
+ if root_ci is not None:
+ self._volume_api.detach(context, root_ci['serial'])
+ self._volume_api.delete(context, root_ci['serial'])
+
+ # We need to clear the block device mapping for the root device
+ bdmobj = objects.BlockDeviceMapping()
+ bdm = bdmobj.get_by_volume_id(context, root_ci['serial'])
+ bdm.destroy(context)
+
+ instance.task_state = task_states.REBUILD_SPAWNING
+ instance.save(
+ expected_task_state=[task_states.REBUILD_BLOCK_DEVICE_MAPPING])
+
+ if recreate:
+ inst_type = flavor_obj.Flavor.get_by_id(
+ nova_context.get_admin_context(read_deleted='yes'),
+ instance['instance_type_id'])
+ extra_specs = inst_type['extra_specs'].copy()
+
+ instance.system_metadata['rebuilding'] = False
+ self._create_config(context, instance, network_info,
+ root_ci, None)
+ del instance.system_metadata['evac_from']
+ instance.save()
+ else:
+ instance.system_metadata['rebuilding'] = True
+ self.spawn(context, instance, image_meta, injected_files,
+ admin_password, network_info, block_device_info)
+ greenthread.sleep(15)
+ self.power_off(instance)
+
+ del instance.system_metadata['rebuilding']
+ name = instance['name']
+ zone = self._get_zone_by_name(name)
+ if zone is None:
+ raise exception.InstanceNotFound(instance_id=name)
+
+ if recreate:
+ zone.attach(['-x', 'initialize-hostdata'])
+
+ instance_uuid = instance.uuid
+ rootmp = instance['root_device_name']
+ for entry in bdms:
+ if (entry['connection_info'] is None or
+ rootmp == entry['device_name']):
+ continue
+
+ connection_info = jsonutils.loads(entry['connection_info'])
+ mount = entry['device_name']
+ self.attach_volume(context, connection_info, instance, mount)
+
+ self._power_on(instance)
+
+ if admin_password is not None:
+ # Because there is no way to make sure a zone is ready upon
+ # returning from a boot request. We must give the zone a few
+ # seconds to boot before attempting to set the admin password.
+ greenthread.sleep(15)
+ self.set_admin_password(instance, admin_password)
def _get_extra_specs(self, instance):
"""Retrieve extra_specs of an instance."""
@@ -1210,7 +1337,8 @@
tstate = instance['task_state']
if tstate not in [task_states.RESIZE_FINISH,
task_states.RESIZE_REVERTING,
- task_states.RESIZE_MIGRATING]:
+ task_states.RESIZE_MIGRATING,
+ task_states.REBUILD_SPAWNING]:
subnet_uuid = port['fixed_ips'][0]['subnet_id']
subnet = network_plugin.show_subnet(subnet_uuid)['subnet']
@@ -1311,7 +1439,10 @@
tstate = instance['task_state']
if tstate not in [task_states.RESIZE_FINISH,
task_states.RESIZE_REVERTING,
- task_states.RESIZE_MIGRATING]:
+ task_states.RESIZE_MIGRATING,
+ task_states.REBUILD_SPAWNING] or \
+ (tstate == task_states.REBUILD_SPAWNING and
+ instance.system_metadata['rebuilding']):
sc_profile = extra_specs.get('install:sc_profile')
if sc_profile is not None:
if os.path.isfile(sc_profile):
@@ -1772,6 +1903,18 @@
block_device_info)
return
+ # A destroy is issued for the original zone for an evac case. If
+ # the evac fails we need to protect the zone from deletion when
+ # power comes back on.
+ evac_from = instance.system_metadata.get('evac_from')
+ if evac_from is not None and instance['task_state'] is None:
+ instance.host = evac_from
+ instance.node = evac_from
+ del instance.system_metadata['evac_from']
+ instance.save()
+
+ return
+
try:
# These methods log if problems occur so no need to double log
# here. Just catch any stray exceptions and allow destroy to
@@ -3275,7 +3418,17 @@
:param instance: nova.objects.instance.Instance
:param new_password: the new password
"""
- raise NotImplementedError()
+ name = instance['name']
+ zone = self._get_zone_by_name(name)
+ if zone is None:
+ raise exception.InstanceNotFound(instance_id=name)
+
+ if zone.state == ZONE_STATE_RUNNING:
+ out, err = utils.execute('/usr/sbin/zlogin', '-S', name,
+ '/usr/bin/passwd', '-p', "'%s'" %
+ sha256_crypt.encrypt(new_pass))
+ else:
+ raise exception.InstanceNotRunning(instance_id=name)
def inject_file(self, instance, b64_path, b64_contents):
"""Writes a file on the specified instance.
@@ -3604,7 +3757,28 @@
Used in rebuild for HA implementation and required for validation
of access to instance shared disk files
"""
- return False
+ bdmobj = objects.BlockDeviceMappingList
+ bdms = bdmobj.get_by_instance_uuid(
+ nova_context.get_admin_context(),
+ instance['uuid'])
+
+ root_ci = None
+ rootmp = instance['root_device_name']
+ for entry in bdms:
+ if entry['connection_info'] is None:
+ continue
+
+ if entry['device_name'] == rootmp:
+ root_ci = jsonutils.loads(entry['connection_info'])
+ break
+
+ if root_ci is None:
+ msg = (_("Unable to find the root device for instance '%s'.")
+ % instance['name'])
+ raise exception.NovaException(msg)
+
+ driver_type = root_ci['driver_volume_type']
+ return driver_type in shared_storage
def register_event_listener(self, callback):
"""Register a callback to receive events.