def test__client_is_cached(self, mock_get_client): mock_get_client.side_effect = get_new_fake_client ironicclient = client_wrapper.IronicClientWrapper() first_client = ironicclient._get_client() second_client = ironicclient._get_client() self.assertEqual(id(first_client), id(second_client))
def test_call_uses_cached_client(self, mock_get_client): mock_get_client.side_effect = get_new_fake_client ironicclient = client_wrapper.IronicClientWrapper() for n in range(0, 4): ironicclient.call("node.list") self.assertEqual(1, mock_get_client.call_count)
def spawn(self, context, instance, image_meta, injected_files, admin_password, network_info=None, block_device_info=None): """Deploy an instance. :param context: The security context. :param instance: The instance object. :param image_meta: Image dict returned by nova.image.glance that defines the image from which to boot this instance. :param injected_files: User files to inject into instance. Ignored by this driver. :param admin_password: Administrator password to set in instance. Ignored by this driver. :param network_info: Instance network information. :param block_device_info: Instance block device information. Ignored by this driver. """ # The compute manager is meant to know the node uuid, so missing uuid # is a significant issue. It may mean we've been passed the wrong data. node_uuid = instance.get('node') if not node_uuid: raise ironic.exc.BadRequest( _("Ironic node uuid not supplied to " "driver for instance %s.") % instance['uuid']) icli = client_wrapper.IronicClientWrapper() node = icli.call("node.get", node_uuid) flavor = objects.Flavor.get_by_id(context, instance['instance_type_id']) self._add_driver_fields(node, instance, image_meta, flavor) # NOTE(Shrews): The default ephemeral device needs to be set for # services (like cloud-init) that depend on it being returned by the # metadata server. Addresses bug https://launchpad.net/bugs/1324286. if flavor['ephemeral_gb']: instance.default_ephemeral_device = '/dev/sda1' instance.save() # validate we are ready to do the deploy validate_chk = icli.call("node.validate", node_uuid) if not validate_chk.deploy or not validate_chk.power: # something is wrong. undo what we have done self._cleanup_deploy(context, node, instance, network_info) raise exception.ValidationError( _("Ironic node: %(id)s failed to validate." " (deploy: %(deploy)s, power: %(power)s)") % { 'id': node.uuid, 'deploy': validate_chk.deploy, 'power': validate_chk.power }) # prepare for the deploy try: self._plug_vifs(node, instance, network_info) self._start_firewall(instance, network_info) except Exception: with excutils.save_and_reraise_exception(): LOG.error( _LE("Error preparing deploy for instance " "%(instance)s on baremetal node %(node)s."), { 'instance': instance['uuid'], 'node': node_uuid }) self._cleanup_deploy(context, node, instance, network_info) # trigger the node deploy try: icli.call("node.set_provision_state", node_uuid, ironic_states.ACTIVE) except Exception as e: with excutils.save_and_reraise_exception(): msg = (_LE("Failed to request Ironic to provision instance " "%(inst)s: %(reason)s"), { 'inst': instance['uuid'], 'reason': six.text_type(e) }) LOG.error(msg) self._cleanup_deploy(context, node, instance, network_info) timer = loopingcall.FixedIntervalLoopingCall(self._wait_for_active, icli, instance) try: timer.start(interval=CONF.ironic.api_retry_interval).wait() except Exception: with excutils.save_and_reraise_exception(): LOG.error( _LE("Error deploying instance %(instance)s on " "baremetal node %(node)s."), { 'instance': instance['uuid'], 'node': node_uuid }) self.destroy(context, instance, network_info)
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): """Rebuild/redeploy an instance. This version of rebuild() allows for supporting the option to preserve the ephemeral partition. We cannot call spawn() from here because it will attempt to set the instance_uuid value again, which is not allowed by the Ironic API. It also requires the instance to not have an 'active' provision state, but we cannot safely change that. Given that, we implement only the portions of spawn() we need within rebuild(). :param context: The security context. :param instance: The instance object. :param image_meta: Image object returned by nova.image.glance that defines the image from which to boot this instance. Ignored by this driver. :param injected_files: User files to inject into instance. Ignored by this driver. :param admin_password: Administrator password to set in instance. Ignored by this driver. :param bdms: block-device-mappings to use for rebuild. Ignored by this driver. :param detach_block_devices: function to detach block devices. See nova.compute.manager.ComputeManager:_rebuild_default_impl for usage. Ignored by this driver. :param attach_block_devices: function to attach block devices. See nova.compute.manager.ComputeManager:_rebuild_default_impl for usage. Ignored by this driver. :param network_info: Instance network information. Ignored by this driver. :param recreate: Boolean value; if True the instance is recreated on a new hypervisor - all the cleanup of old state is skipped. Ignored by this driver. :param block_device_info: Instance block device information. Ignored by this driver. :param preserve_ephemeral: Boolean value; if True the ephemeral must be preserved on rebuild. """ instance.task_state = task_states.REBUILD_SPAWNING instance.save(expected_task_state=[task_states.REBUILDING]) node_uuid = instance.node icli = client_wrapper.IronicClientWrapper() node = icli.call("node.get", node_uuid) flavor = objects.Flavor.get_by_id(context, instance['instance_type_id']) self._add_driver_fields(node, instance, image_meta, flavor, preserve_ephemeral) # Trigger the node rebuild/redeploy. try: icli.call("node.set_provision_state", node_uuid, ironic_states.REBUILD) except ( exception.NovaException, # Retry failed ironic.exc.InternalServerError, # Validations ironic.exc.BadRequest) as e: # Maintenance msg = (_("Failed to request Ironic to rebuild instance " "%(inst)s: %(reason)s") % { 'inst': instance['uuid'], 'reason': six.text_type(e) }) raise exception.InstanceDeployFailure(msg) # Although the target provision state is REBUILD, it will actually go # to ACTIVE once the redeploy is finished. timer = loopingcall.FixedIntervalLoopingCall(self._wait_for_active, icli, instance) timer.start(interval=CONF.ironic.api_retry_interval).wait()
def setUp(self): super(IronicClientWrapperTestCase, self).setUp() self.ironicclient = client_wrapper.IronicClientWrapper() # Do not waste time sleeping cfg.CONF.set_override('api_retry_interval', 0, 'ironic')
def host_passes(self, host_state, spec_obj): ironic_client = ironic_client_wrapper.IronicClientWrapper() ironic_node = ironic_client.call('node.get', host_state.nodename) return (ironic_node.properties.get("available", "") == '*' or ironic_node.properties.get("project_id") == spec_obj.project_id)
def destroy(self, context, instance, network_info, block_device_info=None, destroy_disks=True, migrate_data=None): """Destroy the specified instance, if it can be found. :param context: The security context. :param instance: The instance object. :param network_info: Instance network information. :param block_device_info: Instance block device information. Ignored by this driver. :param destroy_disks: Indicates if disks should be destroyed. Ignored by this driver. :param migrate_data: implementation specific params. Ignored by this driver. """ icli = client_wrapper.IronicClientWrapper() try: node = _validate_instance_and_node(icli, instance) except exception.InstanceNotFound: LOG.warning(_LW("Destroy called on non-existing instance %s."), instance['uuid']) # NOTE(): if nova.compute.ComputeManager._delete_instance() # is called on a non-existing instance, the only way # to delete it is to return from this method # without raising any exceptions. return # need to power on the node before clean local disk if node.power_state == ironic_states.POWER_OFF: try: LOG.info("powering on the node %s" % node.uuid) icli.call("node.set_power_state", node.uuid, "on") timer = loopingcall.FixedIntervalLoopingCall( self._wait_for_power_state, icli, instance, "power on") timer.start(interval=CONF.ironic.api_retry_interval).wait() except Exception: LOG.ERROR("set the node : %s to power on failed." % node.uuid) raise exception.NovaException( _("set the node : %s to " "power on failed.") % node.uuid) LOG.debug(_("Enter clean node disk for baremetal-server: %s"), node) retry_times = CONF.clean_local_disk_retry_times while retry_times > 0: try: self._clean_local_disk(instance) except Exception as e: retry_times -= 1 LOG.error( _("Error clean local disk: %s, instance is %s, " "Traceback is %s, remaining retry times %s." % (e, instance['uuid'], traceback.format_exc(), retry_times))) else: break if node.provision_state in (ironic_states.ACTIVE, ironic_states.DEPLOYFAIL, ironic_states.ERROR, ironic_states.DEPLOYWAIT): self._unprovision(icli, instance, node) self._cleanup_deploy(context, node, instance, network_info)