22464594 Nova driver should support rebuild
authorSean Wilcox <sean.wilcox@oracle.com>
Fri, 22 Jan 2016 12:12:24 -0800
changeset 5313 17713196eb0c
parent 5312 b5fa281dd501
child 5314 8875218ab6e2
22464594 Nova driver should support rebuild
components/openstack/horizon/files/overrides.py
components/openstack/nova/files/solariszones/driver.py
--- a/components/openstack/horizon/files/overrides.py	Fri Jan 22 12:12:23 2016 -0800
+++ b/components/openstack/horizon/files/overrides.py	Fri Jan 22 12:12:24 2016 -0800
@@ -1,4 +1,4 @@
-# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2016, Oracle and/or its affiliates. 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
@@ -86,8 +86,8 @@
     project_tables.TerminateInstance
 )
 
-# Remove 'EditInstanceSecurityGroups', 'TogglePause', 'RebuildInstance' actions
-# from Project/Compute/Instances/Actions
+# Remove 'EditInstanceSecurityGroups', 'TogglePause' actions from 
+# Project/Compute/Instances/Actions
 project_tables.InstancesTable._meta.row_actions = (
     project_tables.StartInstance,
     project_tables.ConfirmResize,
@@ -105,6 +105,7 @@
     project_tables.SoftRebootInstance,
     project_tables.RebootInstance,
     project_tables.StopInstance,
+    project_tables.RebuildInstance,
     project_tables.TerminateInstance
 )
 
--- a/components/openstack/nova/files/solariszones/driver.py	Fri Jan 22 12:12:23 2016 -0800
+++ b/components/openstack/nova/files/solariszones/driver.py	Fri Jan 22 12:12:24 2016 -0800
@@ -804,12 +804,12 @@
                 continue
 
             if not recreate:
-                self._volume_api.detach(context,
-                                        entry['connection_info']['serial'])
-
-        if root_ci is None:
-            msg = (_("Unable to find the root device for instance %s")
-                   % instance.display_name)
+                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
@@ -854,13 +854,12 @@
         :param preserve_ephemeral: True if the default ephemeral storage
                                    partition must be preserved on rebuild
         """
-        # rebuild is not supported yet.
-        if not recreate:
-            raise NotImplementedError()
-
-        extra_specs = self._get_extra_specs(instance)
-        brand = extra_specs.get('zonecfg:brand', ZONE_BRAND_SOLARIS)
-        if recreate and brand == ZONE_BRAND_SOLARIS:
+        if recreate:
+            instance.system_metadata['evac_from'] = instance['launched_on']
+            instance.save()
+            extra_specs = self._get_extra_specs(instance)
+            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)
@@ -871,45 +870,66 @@
         root_ci = self._rebuild_block_devices(context, instance, bdms,
                                               recreate)
 
-        driver_type = root_ci['driver_volume_type']
+        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:
-            # Check to make sure that the root device is actually local.
-            # If not then raise an exception
-            if driver_type in shared_storage:
-                msg = (_("Root device is on shared storage for instance '%s'")
+            # 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)
 
-            if recreate:
-                msg = (_("Evacuate a single host node is not supported"))
-                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'])
+                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])
-        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()
-
-        self._create_config(context, instance, network_info,
-                            root_ci, extra_specs, None)
-
+
+        if recreate:
+            extra_specs = self._get_extra_specs(instance)
+
+            instance.system_metadata['rebuilding'] = False
+            self._create_config(context, instance, network_info,
+                                root_ci, extra_specs, 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)
+            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)
 
-        zone.attach(['-x', 'initialize-hostdata'])
+        if recreate:
+            zone.attach(['-x', 'initialize-hostdata'])
 
         instance_uuid = instance.uuid
         rootmp = instance['root_device_name']
@@ -924,7 +944,7 @@
 
         self._power_on(instance)
 
-        if admin_password:
+        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.
@@ -1414,13 +1434,13 @@
                    % (brand, name)))
             raise exception.NovaException(msg)
 
-        # XXX - Adding the REBUILDING task state, but we may want to
-        # check the sysconfig if the host is completely rebuilding.
         tstate = instance['task_state']
         if tstate not in [task_states.RESIZE_FINISH,
                           task_states.RESIZE_REVERTING,
                           task_states.RESIZE_MIGRATING,
-                          task_states.REBUILD_SPAWNING]:
+                          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):
@@ -1881,6 +1901,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
@@ -3450,12 +3482,11 @@
             raise exception.InstanceNotFound(instance_id=name)
 
         if zone.state == ZONE_STATE_RUNNING:
-            out, err = utils.execute('/usr/sbin/zlogin', name, 'passwd',
-                                     '-p', "'%s'" %
+            out, err = utils.execute('/usr/sbin/zlogin', '-S', name,
+                                     '/usr/bin/passwd', '-p', "'%s'" %
                                      sha256_crypt.encrypt(new_pass))
         else:
-            msg = (_('Unable to change root password: Instance not running'))
-            raise exception.NovaException(msg)
+            raise exception.InstanceNotRunning(instance_id=name)
 
     def inject_file(self, instance, b64_path, b64_contents):
         """Writes a file on the specified instance.
@@ -3785,12 +3816,13 @@
             of access to instance shared disk files
         """
         bdmobj = objects.BlockDeviceMappingList
-        mappings = bdmobj.get_by_instance_uuid(
+        bdms = bdmobj.get_by_instance_uuid(
                 nova_context.get_admin_context(),
                 instance['uuid'])
 
+        root_ci = None
         rootmp = instance['root_device_name']
-        for entry in mappings:
+        for entry in bdms:
             if entry['connection_info'] is None:
                 continue
 
@@ -3799,15 +3831,12 @@
                 break
 
         if root_ci is None:
-            msg = (_("Unable to find the root device for instance %s",
-                     instance.display_name))
+            msg = (_("Unable to find the root device for instance '%s'.")
+                   % instance['name'])
             raise exception.NovaException(msg)
 
         driver_type = root_ci['driver_volume_type']
-        if driver_type in shared_storage:
-            return True
-        else:
-            return False
+        return driver_type in shared_storage
 
     def register_event_listener(self, callback):
         """Register a callback to receive events.