def consume_in_thread(self): """Runs the ZmqProxy service""" ipc_dir = CONF.rpc_zmq_ipc_dir consume_in = "tcp://%s:%s" % \ (CONF.rpc_zmq_bind_address, CONF.rpc_zmq_port) consumption_proxy = InternalContext(None) if not os.path.isdir(ipc_dir): try: utils.execute('mkdir', '-p', ipc_dir, run_as_root=True) utils.execute('chown', "%s:%s" % (os.getuid(), os.getgid()), ipc_dir, run_as_root=True) utils.execute('chmod', '750', ipc_dir, run_as_root=True) except utils.ProcessExecutionError: with excutils.save_and_reraise_exception(): LOG.error(_("Could not create IPC directory %s") % (ipc_dir, )) try: self.register(consumption_proxy, consume_in, zmq.PULL, out_bind=True) except zmq.ZMQError: with excutils.save_and_reraise_exception(): LOG.error(_("Could not create ZeroMQ receiver daemon. " "Socket may already be in use.")) super(ZmqProxy, self).consume_in_thread()
def consume_in_thread(self): """Runs the ZmqProxy service""" ipc_dir = CONF.rpc_zmq_ipc_dir consume_in = "tcp://%s:%s" % \ (CONF.rpc_zmq_bind_address, CONF.rpc_zmq_port) consumption_proxy = InternalContext(None) if not os.path.isdir(ipc_dir): try: utils.execute('mkdir', '-p', ipc_dir, run_as_root=True) utils.execute('chown', "%s:%s" % (os.getuid(), os.getgid()), ipc_dir, run_as_root=True) utils.execute('chmod', '750', ipc_dir, run_as_root=True) except utils.ProcessExecutionError: with excutils.save_and_reraise_exception(): LOG.error( _("Could not create IPC directory %s") % (ipc_dir, )) try: self.register(consumption_proxy, consume_in, zmq.PULL, out_bind=True) except zmq.ZMQError: with excutils.save_and_reraise_exception(): LOG.error( _("Could not create ZeroMQ receiver daemon. " "Socket may already be in use.")) super(ZmqProxy, self).consume_in_thread()
def consume_in_thread(self): """Runs the ZmqProxy service.""" ipc_dir = CONF.rpc_zmq_ipc_dir consume_in = "tcp://%s:%s" % \ (CONF.rpc_zmq_bind_address, CONF.rpc_zmq_port) consumption_proxy = InternalContext(None) try: os.makedirs(ipc_dir) except os.error: if not os.path.isdir(ipc_dir): with excutils.save_and_reraise_exception(): LOG.error( _("Required IPC directory does not exist at" " %s") % (ipc_dir, )) try: self.register(consumption_proxy, consume_in, zmq.PULL) except zmq.ZMQError: if os.access(ipc_dir, os.X_OK): with excutils.save_and_reraise_exception(): LOG.error( _("Permission denied to IPC directory at" " %s") % (ipc_dir, )) with excutils.save_and_reraise_exception(): LOG.error( _("Could not create ZeroMQ receiver daemon. " "Socket may already be in use.")) super(ZmqProxy, self).consume_in_thread()
def deploy(address, port, iqn, lun, image_path, pxe_config_path, root_mb, swap_mb, ephemeral_mb, ephemeral_format, node_uuid, preserve_ephemeral=False): """All-in-one function to deploy a node. :param address: The iSCSI IP address. :param port: The iSCSI port number. :param iqn: The iSCSI qualified name. :param lun: The iSCSI logical unit number. :param image_path: Path for the instance's disk image. :param pxe_config_path: Path for the instance PXE config file. :param root_mb: Size of the root partition in megabytes. :param swap_mb: Size of the swap partition in megabytes. :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0, no ephemeral partition will be created. :param ephemeral_format: The type of file system to format the ephemeral partition. :param node_uuid: node's uuid. Used for logging. :param preserve_ephemeral: If True, no filesystem is written to the ephemeral block device, preserving whatever content it had (if the partition table has not changed). """ dev = get_dev(address, port, iqn, lun) image_mb = get_image_mb(image_path) if image_mb > root_mb: root_mb = image_mb discovery(address, port) login_iscsi(address, port, iqn) try: root_uuid = work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path, node_uuid, preserve_ephemeral) except processutils.ProcessExecutionError as err: with excutils.save_and_reraise_exception(): LOG.error(_("Deploy to address %s failed.") % address) LOG.error(_("Command: %s") % err.cmd) LOG.error(_("StdOut: %r") % err.stdout) LOG.error(_("StdErr: %r") % err.stderr) except exception.InstanceDeployFailure as e: with excutils.save_and_reraise_exception(): LOG.error(_("Deploy to address %s failed.") % address) LOG.error(e) finally: logout_iscsi(address, port, iqn) delete_iscsi(address, port, iqn) switch_pxe_config(pxe_config_path, root_uuid) # Ensure the node started netcat on the port after POST the request. time.sleep(3) notify(address, 10000)
def deploy( address, port, iqn, lun, image_path, pxe_config_path, root_mb, swap_mb, ephemeral_mb, ephemeral_format, preserve_ephemeral=False, ): """All-in-one function to deploy a node. :param address: The iSCSI IP address. :param port: The iSCSI port number. :param iqn: The iSCSI qualified name. :param lun: The iSCSI logical unit number. :param image_path: Path for the instance's disk image. :param pxe_config_path: Path for the instance PXE config file. :param root_mb: Size of the root partition in megabytes. :param swap_mb: Size of the swap partition in megabytes. :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0, no ephemeral partition will be created. :param ephemeral_format: The type of file system to format the ephemeral partition. :param preserve_ephemeral: If True, no filesystem is written to the ephemeral block device, preserving whatever content it had (if the partition table has not changed). """ dev = get_dev(address, port, iqn, lun) image_mb = get_image_mb(image_path) if image_mb > root_mb: root_mb = image_mb discovery(address, port) login_iscsi(address, port, iqn) try: root_uuid = work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path, preserve_ephemeral) except processutils.ProcessExecutionError as err: with excutils.save_and_reraise_exception(): LOG.error(_("Deploy to address %s failed.") % address) LOG.error(_("Command: %s") % err.cmd) LOG.error(_("StdOut: %r") % err.stdout) LOG.error(_("StdErr: %r") % err.stderr) except exception.InstanceDeployFailure as e: with excutils.save_and_reraise_exception(): LOG.error(_("Deploy to address %s failed.") % address) LOG.error(e) finally: logout_iscsi(address, port, iqn) delete_iscsi(address, port, iqn) switch_pxe_config(pxe_config_path, root_uuid) # Ensure the node started netcat on the port after POST the request. time.sleep(3) notify(address, 10000)
def do_node_deploy(self, context, node_id): """RPC method to initiate deployment to a node. :param context: an admin context. :param node_id: the id or uuid of a node. :raises: InstanceDeployFailure """ LOG.debug(_("RPC do_node_deploy called for node %s.") % node_id) with task_manager.acquire(context, node_id, shared=False) as task: node = task.node if node['provision_state'] is not states.NOSTATE: raise exception.InstanceDeployFailure( _("RPC do_node_deploy called for %(node)s, but provision " "state is already %(state)s.") % { 'node': node_id, 'state': node['provision_state'] }) if node.maintenance: raise exception.InstanceDeployFailure( _("RPC do_node_deploy called for %s, but node is in " "maintenance mode.") % node_id) try: task.driver.deploy.validate(task, node) except Exception as e: with excutils.save_and_reraise_exception(): node['last_error'] = \ _("Failed to validate deploy info. Error: %s") % e else: # set target state to expose that work is in progress node['provision_state'] = states.DEPLOYING node['target_provision_state'] = states.DEPLOYDONE node['last_error'] = None finally: node.save(context) try: task.driver.deploy.prepare(task, node) new_state = task.driver.deploy.deploy(task, node) except Exception as e: with excutils.save_and_reraise_exception(): node['last_error'] = _("Failed to deploy. Error: %s") % e node['provision_state'] = states.DEPLOYFAIL node['target_provision_state'] = states.NOSTATE else: # NOTE(deva): Some drivers may return states.DEPLOYWAIT # eg. if they are waiting for a callback if new_state == states.DEPLOYDONE: node['target_provision_state'] = states.NOSTATE node['provision_state'] = states.ACTIVE else: node['provision_state'] = new_state finally: node.save(context)
def do_node_tear_down(self, context, node_id): """RPC method to tear down an existing node deployment. :param context: an admin context. :param node_id: the id or uuid of a node. :raises: InstanceDeployFailure """ LOG.debug(_("RPC do_node_tear_down called for node %s.") % node_id) with task_manager.acquire(context, node_id, shared=False) as task: node = task.node if node['provision_state'] not in [ states.ACTIVE, states.DEPLOYFAIL, states.ERROR, states.DEPLOYWAIT ]: raise exception.InstanceDeployFailure( _("RCP do_node_tear_down " "not allowed for node %(node)s in state %(state)s") % { 'node': node_id, 'state': node['provision_state'] }) try: task.driver.deploy.validate(task, node) except Exception as e: with excutils.save_and_reraise_exception(): node['last_error'] = \ ("Failed to validate info for teardown. Error: %s") % e else: # set target state to expose that work is in progress node['provision_state'] = states.DELETING node['target_provision_state'] = states.DELETED node['last_error'] = None finally: node.save(context) try: task.driver.deploy.clean_up(task, node) new_state = task.driver.deploy.tear_down(task, node) except Exception as e: with excutils.save_and_reraise_exception(): node['last_error'] = \ _("Failed to tear down. Error: %s") % e node['provision_state'] = states.ERROR node['target_provision_state'] = states.NOSTATE else: # NOTE(deva): Some drivers may return states.DELETING # eg. if they are waiting for a callback if new_state == states.DELETED: node['target_provision_state'] = states.NOSTATE node['provision_state'] = states.NOSTATE else: node['provision_state'] = new_state finally: node.save(context)
def set_console_mode(self, context, node_id, enabled): """Enable/Disable the console. :param context: request context. :param node_id: node id or uuid. :param enabled: Boolean value; whether the console is enabled or disabled. :raises: UnsupportedDriverExtension if the node's driver doesn't support console. :raises: InvalidParameterValue when the wrong driver info is specified. """ LOG.debug(_('RPC set_console_mode called for node %(node)s with ' 'enabled %(enabled)s') % {'node': node_id, 'enabled': enabled}) with task_manager.acquire(context, node_id) as task: node = task.node if not getattr(task.driver, 'console', None): exc = exception.UnsupportedDriverExtension(driver=node.driver, extension='console') node.last_error = exc.format_message() node.save(context) raise exc try: task.driver.console.validate(task, node) except exception.InvalidParameterValue as e: with excutils.save_and_reraise_exception(): node.last_error = (_("Failed to validate console info. " "Error: %s") % e) node.save(context) try: if enabled and not node.console_enabled: task.driver.console.start_console(task, node) elif not enabled and node.console_enabled: task.driver.console.stop_console(task, node) else: op = _('enabled') if enabled else _('disabled') LOG.info(_("No console action was triggered because the " "console is already %s") % op) except Exception as e: with excutils.save_and_reraise_exception(): op = _('enabling') if enabled else _('disabling') msg = (_('Error %(op)s the console on node %(node)s. ' 'Reason: %(error)s') % {'op': op, 'node': node.uuid, 'error': e}) node.last_error = msg else: node.console_enabled = enabled node.last_error = None finally: node.save(context)
def do_node_tear_down(self, context, node_obj): """RPC method to tear down an existing node deployment. :param context: an admin context. :param node_obj: an RPC-style node object. :raises: InstanceDeployFailure """ node_id = node_obj.get('uuid') LOG.debug(_("RPC do_node_tear_down called for node %s.") % node_id) with task_manager.acquire(context, node_id, shared=False) as task: node = task.node if node['provision_state'] not in [states.ACTIVE, states.DEPLOYFAIL, states.ERROR]: raise exception.InstanceDeployFailure(_( "RCP do_node_tear_down " "not allowed for node %(node)s in state %(state)s") % {'node': node_id, 'state': node['provision_state']}) try: task.driver.deploy.validate(node) except Exception as e: with excutils.save_and_reraise_exception(): node['last_error'] = \ ("Failed to validate info for teardown. Error: %s") % e else: # set target state to expose that work is in progress node['provision_state'] = states.DELETING node['target_provision_state'] = states.DELETED node['last_error'] = None finally: node.save(context) try: new_state = task.driver.deploy.tear_down(task, node) except Exception as e: with excutils.save_and_reraise_exception(): node['last_error'] = \ _("Failed to tear down. Error: %s") % e node['provision_state'] = states.ERROR node['target_provision_state'] = states.NOSTATE else: # NOTE(deva): Some drivers may return states.DELETING # eg. if they are waiting for a callback if new_state == states.DELETED: node['target_provision_state'] = states.NOSTATE node['provision_state'] = states.NOSTATE else: node['provision_state'] = new_state finally: node.save(context)
def do_node_deploy(self, context, node_obj): """RPC method to initiate deployment to a node. :param context: an admin context. :param node_obj: an RPC-style node object. :raises: InstanceDeployFailure """ node_id = node_obj.get('uuid') LOG.debug(_("RPC do_node_deploy called for node %s.") % node_id) with task_manager.acquire(context, node_id, shared=False) as task: node = task.node if node['provision_state'] is not states.NOSTATE: raise exception.InstanceDeployFailure(_( "RPC do_node_deploy called for %(node)s, but provision " "state is already %(state)s.") % {'node': node_id, 'state': node['provision_state']}) try: task.driver.deploy.validate(node) except Exception as e: with excutils.save_and_reraise_exception(): node['last_error'] = \ _("Failed to validate deploy info. Error: %s") % e else: # set target state to expose that work is in progress node['provision_state'] = states.DEPLOYING node['target_provision_state'] = states.DEPLOYDONE node['last_error'] = None finally: node.save(context) try: new_state = task.driver.deploy.deploy(task, node) except Exception as e: with excutils.save_and_reraise_exception(): node['last_error'] = _("Failed to deploy. Error: %s") % e node['provision_state'] = states.ERROR node['target_provision_state'] = states.NOSTATE else: # NOTE(deva): Some drivers may return states.DEPLOYING # eg. if they are waiting for a callback if new_state == states.DEPLOYDONE: node['target_provision_state'] = states.NOSTATE node['provision_state'] = states.ACTIVE else: node['provision_state'] = new_state finally: node.save(context)
def destroy_disk_metadata(dev, node_uuid): """Destroy metadata structures on node's disk. Ensure that node's disk appears to be blank without zeroing the entire drive. To do this we will zero: - the first 18KiB to clear MBR / GPT data - the last 18KiB to clear GPT and other metadata like: LVM, veritas, MDADM, DMRAID, ... """ # NOTE(NobodyCam): This is needed to work around bug: # https://bugs.launchpad.net/ironic/+bug/1317647 try: utils.execute('dd', 'if=/dev/zero', 'of=%s' % dev, 'bs=512', 'count=36', run_as_root=True, check_exit_code=[0]) except processutils.ProcessExecutionError as err: with excutils.save_and_reraise_exception(): LOG.error(_("Failed to erase beginning of disk for node " "%(node)s. Command: %(command)s. Error: %(error)s."), {'node': node_uuid, 'command': err.cmd, 'error': err.stderr}) # now wipe the end of the disk. # get end of disk seek value try: block_sz = get_dev_block_size(dev) except processutils.ProcessExecutionError as err: with excutils.save_and_reraise_exception(): LOG.error(_("Failed to get disk block count for node %(node)s. " "Command: %(command)s. Error: %(error)s."), {'node': node_uuid, 'command': err.cmd, 'error': err.stderr}) else: seek_value = block_sz - 36 try: utils.execute('dd', 'if=/dev/zero', 'of=%s' % dev, 'bs=512', 'count=36', 'seek=%d' % seek_value, run_as_root=True, check_exit_code=[0]) except processutils.ProcessExecutionError as err: with excutils.save_and_reraise_exception(): LOG.error(_("Failed to erase the end of the disk on node " "%(node)s. Command: %(command)s. " "Error: %(error)s."), {'node': node_uuid, 'command': err.cmd, 'error': err.stderr})
def destroy(self, instance, network_info, block_device_info=None): context = nova_context.get_admin_context() try: node = _get_baremetal_node_by_instance_uuid(instance['uuid']) except exception.InstanceNotFound: LOG.warning(_("Destroy called on non-existing instance %s") % instance['uuid']) return try: self.driver.deactivate_node(context, node, instance) self.power_off(instance, node) self.driver.deactivate_bootloader(context, node, instance) self.driver.destroy_images(context, node, instance) self._detach_block_devices(instance, block_device_info) self._stop_firewall(instance, network_info) self._unplug_vifs(instance, network_info) _update_state(context, node, None, states.DELETED) except Exception as e: with excutils.save_and_reraise_exception(): try: LOG.error(_("Error from baremetal driver " "during destroy: %s") % e) _update_state(context, node, instance, states.ERROR) except Exception: LOG.error(_("Error while recording destroy failure in " "baremetal database: %s") % e)
def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path, preserve_ephemeral=False): """Create partitions and copy an image to the root partition. :param dev: Path for the device to work on. :param root_mb: Size of the root partition in megabytes. :param swap_mb: Size of the swap partition in megabytes. :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0, no ephemeral partition will be created. :param ephemeral_format: The type of file system to format the ephemeral partition. :param image_path: Path for the instance's disk image. :param preserve_ephemeral: If True, no filesystem is written to the ephemeral block device, preserving whatever content it had (if the partition table has not changed). """ # NOTE(lucasagomes): When there's an ephemeral partition we want # root to be last because that would allow root to resize and make it # safer to do takeovernode with slightly larger images if ephemeral_mb: ephemeral_part = "%s-part1" % dev swap_part = "%s-part2" % dev root_part = "%s-part3" % dev else: root_part = "%s-part1" % dev swap_part = "%s-part2" % dev if not is_block_device(dev): raise exception.InstanceDeployFailure( _("Parent device '%s' not found") % dev) make_partitions(dev, root_mb, swap_mb, ephemeral_mb) if not is_block_device(root_part): raise exception.InstanceDeployFailure( _("Root device '%s' not found") % root_part) if not is_block_device(swap_part): raise exception.InstanceDeployFailure( _("Swap device '%s' not found") % swap_part) if ephemeral_mb and not is_block_device(ephemeral_part): raise exception.InstanceDeployFailure( _("Ephemeral device '%s' not found") % ephemeral_part) dd(image_path, root_part) mkswap(swap_part) if ephemeral_mb and not preserve_ephemeral: mkfs_ephemeral(ephemeral_part, ephemeral_format) try: root_uuid = block_uuid(root_part) except processutils.ProcessExecutionError: with excutils.save_and_reraise_exception(): LOG.error(_("Failed to detect root device UUID.")) return root_uuid
def patch(self, uuid, patch): """Update an existing node.""" if self._from_chassis: raise exception.OperationNotPermitted node = objects.Node.get_by_uuid(pecan.request.context, uuid) node_dict = node.as_dict() utils.validate_patch(patch) patch_obj = jsonpatch.JsonPatch(patch) # Prevent states from being updated state_rel_path = ['/power_state', '/target_power_state', '/provision_state', '/target_provision_state'] if any(p['path'] in state_rel_path for p in patch_obj): raise wsme.exc.ClientSideError(_("Changing states is not allowed " "here; You must use the " "nodes/%s/state interface.") % uuid) # Prevent node from being updated when there's a state # change in progress if any(node.get(tgt) for tgt in ["target_power_state", "target_provision_state"]): raise wsme.exc.ClientSideError(_("Can not update node %s while " "a state transition is in " "progress.") % uuid, status_code=409) try: patched_node = jsonpatch.apply_patch(node_dict, patch_obj) except jsonpatch.JsonPatchException as e: LOG.exception(e) raise wsme.exc.ClientSideError(_("Patching Error: %s") % e) try: self. _convert_chassis_uuid_to_id(patched_node) defaults = objects.Node.get_defaults() for key in defaults: # Internal values that shouldn't be part of the patch if key in ['id', 'updated_at', 'created_at']: continue # In case of a remove operation, add the missing fields back # to the document with their default value if key in node_dict and key not in patched_node: patched_node[key] = defaults[key] # Update only the fields that have changed if node[key] != patched_node[key]: node[key] = patched_node[key] node = pecan.request.rpcapi.update_node(pecan.request.context, node) except exception.IronicException as e: with excutils.save_and_reraise_exception(): LOG.exception(e) return Node.convert_with_links(node)
def _do_node_tear_down(self, context, task): """Internal RPC method to tear down an existing node deployment.""" node = task.node try: task.driver.deploy.clean_up(task) new_state = task.driver.deploy.tear_down(task) except Exception as e: with excutils.save_and_reraise_exception(): LOG.warning(_('Error in tear_down of node %(node)s: %(err)s'), {'node': task.node.uuid, 'err': e}) node.last_error = _("Failed to tear down. Error: %s") % e node.provision_state = states.ERROR node.target_provision_state = states.NOSTATE else: # NOTE(deva): Some drivers may return states.DELETING # eg. if they are waiting for a callback if new_state == states.DELETED: node.target_provision_state = states.NOSTATE node.provision_state = states.NOSTATE LOG.info(_LI('Successfully unprovisioned node %(node)s with ' 'instance %(instance)s.'), {'node': node.uuid, 'instance': node.instance_uuid}) else: node.provision_state = new_state finally: # Clean the instance_info node.instance_info = {} node.save(context)
def _do_node_deploy(self, context, task): """Prepare the environment and deploy a node.""" node = task.node try: task.driver.deploy.prepare(task) new_state = task.driver.deploy.deploy(task) except Exception as e: with excutils.save_and_reraise_exception(): LOG.warning(_('Error in deploy of node %(node)s: %(err)s'), {'node': task.node.uuid, 'err': e}) node.last_error = _("Failed to deploy. Error: %s") % e node.provision_state = states.DEPLOYFAIL node.target_provision_state = states.NOSTATE else: # NOTE(deva): Some drivers may return states.DEPLOYWAIT # eg. if they are waiting for a callback if new_state == states.DEPLOYDONE: node.target_provision_state = states.NOSTATE node.provision_state = states.ACTIVE LOG.info(_LI('Successfully deployed node %(node)s with ' 'instance %(instance)s.'), {'node': node.uuid, 'instance': node.instance_uuid}) else: node.provision_state = new_state finally: node.save(context)
def change_node_power_state(self, context, node_id, new_state): """RPC method to encapsulate changes to a node's state. Perform actions such as power on, power off. The validation and power action are performed in background (async). Once the power action is finished and successful, it updates the power_state for the node with the new power state. :param context: an admin context. :param node_id: the id or uuid of a node. :param new_state: the desired power state of the node. :raises: NoFreeConductorWorker when there is no free worker to start async task. """ LOG.debug(_("RPC change_node_power_state called for node %(node)s. " "The desired new state is %(state)s.") % {'node': node_id, 'state': new_state}) task = task_manager.TaskManager(context, node_id, shared=False) try: # Start requested action in the background. thread = self._spawn_worker(utils.node_power_action, task, task.node, new_state) # Release node lock at the end. thread.link(lambda t: task.release_resources()) except Exception: with excutils.save_and_reraise_exception(): # Release node lock if error occurred. task.release_resources()
def post(self, node): """Create a new node. :param node: a node within the request body. """ if self._from_chassis: raise exception.OperationNotPermitted # NOTE(deva): get_topic_for checks if node.driver is in the hash ring # and raises NoValidHost if it is not. # We need to ensure that node has a UUID before it can # be mapped onto the hash ring. if not node.uuid: node.uuid = utils.generate_uuid() try: pecan.request.rpcapi.get_topic_for(node) except exception.NoValidHost as e: # NOTE(deva): convert from 404 to 400 because client can see # list of available drivers and shouldn't request # one that doesn't exist. e.code = 400 raise e try: new_node = pecan.request.dbapi.create_node(node.as_dict()) except Exception as e: with excutils.save_and_reraise_exception(): LOG.exception(e) return Node.convert_with_links(new_node)
def _get_power_state(node): """Returns the current power state of the node :param node: The node. :returns: power state, one of :mod: `ironic.common.states`. :raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are missing. """ client = drac_common.get_wsman_client(node) options = pywsman.ClientOptions() filter_query = ('select EnabledState,ElementName from CIM_ComputerSystem ' 'where Name="srv:system"') try: doc = client.wsman_enumerate(resource_uris.DCIM_ComputerSystem, options, filter_query=filter_query) except exception.DracClientError as exc: with excutils.save_and_reraise_exception(): LOG.error(_LE('DRAC driver failed to get power state for node ' '%(node_uuid)s. Reason: %(error)s.'), {'node_uuid': node.uuid, 'error': exc}) enabled_state = drac_common.find_xml(doc, 'EnabledState', resource_uris.DCIM_ComputerSystem) return POWER_STATES[enabled_state.text]
def wrapped(self, context, *args, **kw): # Don't store self or context in the payload, it now seems to # contain confidential information. try: return f(self, context, *args, **kw) except Exception, e: with excutils.save_and_reraise_exception(): if notifier: payload = dict(exception=e) call_dict = safe_utils.getcallargs(f, *args, **kw) cleansed = _cleanse_dict(call_dict) payload.update({'args': cleansed}) # Use a temp vars so we don't shadow # our outer definitions. temp_level = level if not temp_level: temp_level = notifier.ERROR temp_type = event_type if not temp_type: # If f has multiple decorators, they must use # functools.wraps to ensure the name is # propagated. temp_type = f.__name__ notifier.notify(context, publisher_id, temp_type, temp_level, payload)
def init_host(self): self.dbapi = dbapi.get_instance() self.driver_factory = driver_factory.DriverFactory() self.drivers = self.driver_factory.names """List of driver names which this conductor supports.""" try: self.dbapi.register_conductor({'hostname': self.host, 'drivers': self.drivers}) except exception.ConductorAlreadyRegistered: LOG.warn(_("A conductor with hostname %(hostname)s " "was previously registered. Updating registration") % {'hostname': self.host}) self.dbapi.unregister_conductor(self.host) self.dbapi.register_conductor({'hostname': self.host, 'drivers': self.drivers}) self.ring_manager = hash.HashRingManager() """Consistent hash ring which maps drivers to conductors.""" self._worker_pool = greenpool.GreenPool( size=CONF.conductor.workers_pool_size) """GreenPool of background workers for performing tasks async.""" # Spawn a dedicated greenthread for the keepalive try: self._keepalive_evt = threading.Event() self._spawn_worker(self._conductor_service_record_keepalive) except exception.NoFreeConductorWorker: with excutils.save_and_reraise_exception(): LOG.critical(_('Failed to start keepalive')) self.del_host()
def change_node_power_state(self, context, node_id, new_state): """RPC method to encapsulate changes to a node's state. Perform actions such as power on, power off. The validation and power action are performed in background (async). Once the power action is finished and successful, it updates the power_state for the node with the new power state. :param context: an admin context. :param node_id: the id or uuid of a node. :param new_state: the desired power state of the node. :raises: NoFreeConductorWorker when there is no free worker to start async task. """ LOG.debug( _("RPC change_node_power_state called for node %(node)s. " "The desired new state is %(state)s.") % { 'node': node_id, 'state': new_state }) task = task_manager.TaskManager(context, node_id, shared=False) try: # Start requested action in the background. thread = self._spawn_worker(utils.node_power_action, task, task.node, new_state) # Release node lock at the end. thread.link(lambda t: task.release_resources()) except Exception: with excutils.save_and_reraise_exception(): # Release node lock if error occurred. task.release_resources()
def destroy(self, instance, network_info, block_device_info=None): context = nova_context.get_admin_context() try: node = _get_baremetal_node_by_instance_uuid(instance['uuid']) except exception.InstanceNotFound: LOG.warning( _("Destroy called on non-existing instance %s") % instance['uuid']) return try: self.driver.deactivate_node(context, node, instance) self.power_off(instance, node) self.driver.deactivate_bootloader(context, node, instance) self.driver.destroy_images(context, node, instance) self._detach_block_devices(instance, block_device_info) self._stop_firewall(instance, network_info) self._unplug_vifs(instance, network_info) _update_state(context, node, None, states.DELETED) except Exception as e: with excutils.save_and_reraise_exception(): try: LOG.error( _("Error from baremetal driver " "during destroy: %s") % e) _update_state(context, node, instance, states.ERROR) except Exception: LOG.error( _("Error while recording destroy failure in " "baremetal database: %s") % e)
def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None and self._spawn_method is not None: # Spawn a worker to complete the task # The linked callback below will be called whenever: # - background task finished with no errors. # - background task has crashed with exception. # - callback was added after the background task has # finished or crashed. While eventlet currently doesn't # schedule the new thread until the current thread blocks # for some reason, this is true. # All of the above are asserted in tests such that we'll # catch if eventlet ever changes this behavior. thread = None try: thread = self._spawn_method(*self._spawn_args, **self._spawn_kwargs) # NOTE(comstud): Trying to use a lambda here causes # the callback to not occur for some reason. This # also makes it easier to test. thread.link(self._thread_release_resources) # Don't unlock! The unlock will occur when the # thread finshes. return except Exception: with excutils.save_and_reraise_exception(): if thread is not None: # This means the link() failed for some # reason. Nuke the thread. thread.cancel() self.release_resources() self.release_resources()
def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path, node_uuid, preserve_ephemeral=False): """Create partitions and copy an image to the root partition. :param dev: Path for the device to work on. :param root_mb: Size of the root partition in megabytes. :param swap_mb: Size of the swap partition in megabytes. :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0, no ephemeral partition will be created. :param ephemeral_format: The type of file system to format the ephemeral partition. :param image_path: Path for the instance's disk image. :param node_uuid: node's uuid. Used for logging. :param preserve_ephemeral: If True, no filesystem is written to the ephemeral block device, preserving whatever content it had (if the partition table has not changed). """ if not is_block_device(dev): raise exception.InstanceDeployFailure(_("Parent device '%s' not found") % dev) # the only way for preserve_ephemeral to be set to true is if we are # rebuilding an instance with --preserve_ephemeral. commit = not preserve_ephemeral # now if we are committing the changes to disk clean first. if commit: destroy_disk_metadata(dev, node_uuid) part_dict = make_partitions(dev, root_mb, swap_mb, ephemeral_mb, commit=commit) ephemeral_part = part_dict.get('ephemeral') swap_part = part_dict.get('swap') root_part = part_dict.get('root') if not is_block_device(root_part): raise exception.InstanceDeployFailure(_("Root device '%s' not found") % root_part) if swap_part and not is_block_device(swap_part): raise exception.InstanceDeployFailure(_("Swap device '%s' not found") % swap_part) if ephemeral_part and not is_block_device(ephemeral_part): raise exception.InstanceDeployFailure( _("Ephemeral device '%s' not found") % ephemeral_part) dd(image_path, root_part) if swap_part: mkswap(swap_part) if ephemeral_part and not preserve_ephemeral: mkfs_ephemeral(ephemeral_part, ephemeral_format) try: root_uuid = block_uuid(root_part) except processutils.ProcessExecutionError: with excutils.save_and_reraise_exception(): LOG.error(_("Failed to detect root device UUID.")) return root_uuid
def post(self, chassis): """Create a new chassis.""" try: new_chassis = pecan.request.dbapi.create_chassis(chassis.as_dict()) except Exception as e: with excutils.save_and_reraise_exception(): LOG.exception(e) return Chassis.convert_with_links(new_chassis)
def set_console_mode(self, context, node_id, enabled): """Enable/Disable the console. Validate driver specific information synchronously, and then spawn a background worker to set console mode asynchronously. :param context: request context. :param node_id: node id or uuid. :param enabled: Boolean value; whether the console is enabled or disabled. :raises: UnsupportedDriverExtension if the node's driver doesn't support console. :raises: InvalidParameterValue when the wrong driver info is specified. :raises: NoFreeConductorWorker when there is no free worker to start async task """ LOG.debug( _('RPC set_console_mode called for node %(node)s with ' 'enabled %(enabled)s') % { 'node': node_id, 'enabled': enabled }) task = task_manager.TaskManager(context, node_id, shared=False) node = task.node try: if not getattr(task.driver, 'console', None): exc = exception.UnsupportedDriverExtension(driver=node.driver, extension='console') node.last_error = exc.format_message() node.save(context) raise exc task.driver.console.validate(task, node) if enabled == node.console_enabled: op = _('enabled') if enabled else _('disabled') LOG.info( _("No console action was triggered because the " "console is already %s") % op) else: node.last_error = None node.save(context) # Start the requested action in the background. thread = self._spawn_worker(self._set_console_mode, task, enabled) # Release node lock at the end. thread.link(lambda t: task.release_resources()) except Exception: with excutils.save_and_reraise_exception(): # Release node lock if error occurred. task.release_resources()
def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path, preserve_ephemeral=False): """Create partitions and copy an image to the root partition. :param dev: Path for the device to work on. :param root_mb: Size of the root partition in megabytes. :param swap_mb: Size of the swap partition in megabytes. :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0, no ephemeral partition will be created. :param ephemeral_format: The type of file system to format the ephemeral partition. :param image_path: Path for the instance's disk image. :param preserve_ephemeral: If True, no filesystem is written to the ephemeral block device, preserving whatever content it had (if the partition table has not changed). """ # NOTE(lucasagomes): When there's an ephemeral partition we want # root to be last because that would allow root to resize and make it # safer to do takeovernode with slightly larger images if ephemeral_mb: ephemeral_part = "%s-part1" % dev swap_part = "%s-part2" % dev root_part = "%s-part3" % dev else: root_part = "%s-part1" % dev swap_part = "%s-part2" % dev if not is_block_device(dev): raise exception.InstanceDeployFailure(_("Parent device '%s' not found") % dev) make_partitions(dev, root_mb, swap_mb, ephemeral_mb) if not is_block_device(root_part): raise exception.InstanceDeployFailure(_("Root device '%s' not found") % root_part) if not is_block_device(swap_part): raise exception.InstanceDeployFailure(_("Swap device '%s' not found") % swap_part) if ephemeral_mb and not is_block_device(ephemeral_part): raise exception.InstanceDeployFailure( _("Ephemeral device '%s' not found") % ephemeral_part) dd(image_path, root_part) mkswap(swap_part) if ephemeral_mb and not preserve_ephemeral: mkfs_ephemeral(ephemeral_part, ephemeral_format) try: root_uuid = block_uuid(root_part) except processutils.ProcessExecutionError: with excutils.save_and_reraise_exception(): LOG.error(_("Failed to detect root device UUID.")) return root_uuid
def do_node_deploy(self, context, node_id): """RPC method to initiate deployment to a node. Initiate the deployment of a node. Validations are done synchronously and the actual deploy work is performed in background (asynchronously). :param context: an admin context. :param node_id: the id or uuid of a node. :raises: InstanceDeployFailure :raises: NodeInMaintenance if the node is in maintenance mode. :raises: NoFreeConductorWorker when there is no free worker to start async task. """ LOG.debug(_("RPC do_node_deploy called for node %s.") % node_id) # NOTE(comstud): If the _sync_power_states() periodic task happens # to have locked this node, we'll fail to acquire the lock. The # client should perhaps retry in this case unless we decide we # want to add retries or extra synchronization here. task = task_manager.TaskManager(context, node_id, shared=False) node = task.node try: if node.provision_state is not states.NOSTATE: raise exception.InstanceDeployFailure(_( "RPC do_node_deploy called for %(node)s, but provision " "state is already %(state)s.") % {'node': node.uuid, 'state': node.provision_state}) if node.maintenance: raise exception.NodeInMaintenance(op=_('provisioning'), node=node.uuid) try: task.driver.deploy.validate(task, node) except exception.InvalidParameterValue as e: raise exception.InstanceDeployFailure(_( "RPC do_node_deploy failed to validate deploy info. " "Error: %(msg)s") % {'msg': e}) # Set target state to expose that work is in progress node.provision_state = states.DEPLOYING node.target_provision_state = states.DEPLOYDONE node.last_error = None node.save(context) # Start requested action in the background. thread = self._spawn_worker(self._do_node_deploy, context, task) # Release node lock at the end. thread.link(lambda t: task.release_resources()) except Exception: with excutils.save_and_reraise_exception(): # Release node lock if error occurred. task.release_resources()
def _serialize(data): """Serialization wrapper. We prefer using JSON, but it cannot encode all types. Error if a developer passes us bad data. """ try: return jsonutils.dumps(data, ensure_ascii=True) except TypeError: with excutils.save_and_reraise_exception(): LOG.error(_("JSON serialization failed."))
def remove_path_on_error(path): """Protect code that wants to operate on PATH atomically. Any exception will cause PATH to be removed. :param path: File to work with """ try: yield except Exception: with excutils.save_and_reraise_exception(): delete_if_exists(path)
def _serialize(data): """ Serialization wrapper We prefer using JSON, but it cannot encode all types. Error if a developer passes us bad data. """ try: return jsonutils.dumps(data, ensure_ascii=True) except TypeError: with excutils.save_and_reraise_exception(): LOG.error(_("JSON serialization failed."))
def post(self, chassis): """Create a new chassis. :param chassis: a chassis within the request body. """ try: new_chassis = pecan.request.dbapi.create_chassis(chassis.as_dict()) except Exception as e: with excutils.save_and_reraise_exception(): LOG.exception(e) return Chassis.convert_with_links(new_chassis)
def _make_password_file(password): try: fd, path = tempfile.mkstemp() os.fchmod(fd, stat.S_IRUSR | stat.S_IWUSR) with os.fdopen(fd, "w") as f: f.write(password) yield path utils.delete_if_exists(path) except Exception: with excutils.save_and_reraise_exception(): utils.delete_if_exists(path)
def do_node_deploy(self, context, node_id): """RPC method to initiate deployment to a node. Initiate the deployment of a node. Validations are done synchronously and the actual deploy work is performed in background (asynchronously). :param context: an admin context. :param node_id: the id or uuid of a node. :raises: InstanceDeployFailure :raises: NodeInMaintenance if the node is in maintenance mode. :raises: NoFreeConductorWorker when there is no free worker to start async task. """ LOG.debug(_("RPC do_node_deploy called for node %s.") % node_id) task = task_manager.TaskManager(context, node_id, shared=False) node = task.node try: if node.provision_state is not states.NOSTATE: raise exception.InstanceDeployFailure( _("RPC do_node_deploy called for %(node)s, but provision " "state is already %(state)s.") % { 'node': node.uuid, 'state': node.provision_state }) if node.maintenance: raise exception.NodeInMaintenance(op=_('provisioning'), node=node.uuid) try: task.driver.deploy.validate(task, node) except exception.InvalidParameterValue as e: raise exception.InstanceDeployFailure( _("RPC do_node_deploy failed to validate deploy info. " "Error: %(msg)s") % {'msg': e}) # Set target state to expose that work is in progress node.provision_state = states.DEPLOYING node.target_provision_state = states.DEPLOYDONE node.last_error = None node.save(context) # Start requested action in the background. thread = self._spawn_worker(self._do_node_deploy, context, task) # Release node lock at the end. thread.link(lambda t: task.release_resources()) except Exception: with excutils.save_and_reraise_exception(): # Release node lock if error occurred. task.release_resources()
def set_console_mode(self, context, node_id, enabled): """Enable/Disable the console. Validate driver specific information synchronously, and then spawn a background worker to set console mode asynchronously. :param context: request context. :param node_id: node id or uuid. :param enabled: Boolean value; whether the console is enabled or disabled. :raises: UnsupportedDriverExtension if the node's driver doesn't support console. :raises: InvalidParameterValue when the wrong driver info is specified. :raises: NoFreeConductorWorker when there is no free worker to start async task """ LOG.debug(_('RPC set_console_mode called for node %(node)s with ' 'enabled %(enabled)s') % {'node': node_id, 'enabled': enabled}) task = task_manager.TaskManager(context, node_id, shared=False) node = task.node try: if not getattr(task.driver, 'console', None): exc = exception.UnsupportedDriverExtension(driver=node.driver, extension='console') node.last_error = exc.format_message() node.save(context) raise exc task.driver.console.validate(task, node) if enabled == node.console_enabled: op = _('enabled') if enabled else _('disabled') LOG.info(_("No console action was triggered because the " "console is already %s") % op) task.release_resources() else: node.last_error = None node.save(context) # Start the requested action in the background. thread = self._spawn_worker(self._set_console_mode, task, enabled) # Release node lock at the end. thread.link(lambda t: task.release_resources()) except Exception: with excutils.save_and_reraise_exception(): # Release node lock if error occurred. task.release_resources()
def consume_in_thread(self): """Runs the ZmqProxy service.""" ipc_dir = CONF.rpc_zmq_ipc_dir consume_in = "tcp://%s:%s" % (CONF.rpc_zmq_bind_address, CONF.rpc_zmq_port) consumption_proxy = InternalContext(None) try: os.makedirs(ipc_dir) except os.error: if not os.path.isdir(ipc_dir): with excutils.save_and_reraise_exception(): LOG.error(_("Required IPC directory does not exist at" " %s") % (ipc_dir,)) try: self.register(consumption_proxy, consume_in, zmq.PULL) except zmq.ZMQError: if os.access(ipc_dir, os.X_OK): with excutils.save_and_reraise_exception(): LOG.error(_("Permission denied to IPC directory at" " %s") % (ipc_dir,)) with excutils.save_and_reraise_exception(): LOG.error(_("Could not create ZeroMQ receiver daemon. " "Socket may already be in use.")) super(ZmqProxy, self).consume_in_thread()
def do_node_tear_down(self, context, node_id): """RPC method to tear down an existing node deployment. Validate driver specific information synchronously, and then spawn a background worker to tear down the node asynchronously. :param context: an admin context. :param node_id: the id or uuid of a node. :raises: InstanceDeployFailure :raises: NoFreeConductorWorker when there is no free worker to start async task """ LOG.debug(_("RPC do_node_tear_down called for node %s.") % node_id) task = task_manager.TaskManager(context, node_id, shared=False) node = task.node try: if node.provision_state not in [ states.ACTIVE, states.DEPLOYFAIL, states.ERROR, states.DEPLOYWAIT ]: raise exception.InstanceDeployFailure( _("RPC do_node_tear_down " "not allowed for node %(node)s in state %(state)s") % { 'node': node_id, 'state': node.provision_state }) try: task.driver.deploy.validate(task, node) except exception.InvalidParameterValue as e: raise exception.InstanceDeployFailure( _("RPC do_node_tear_down failed to validate deploy info. " "Error: %(msg)s") % {'msg': e}) node.provision_state = states.DELETING node.target_provision_state = states.DELETED node.last_error = None node.save(context) # Start requested action in the background. thread = self._spawn_worker(self._do_node_tear_down, context, task) # Release node lock at the end. thread.link(lambda t: task.release_resources()) except Exception: with excutils.save_and_reraise_exception(): # Release node lock if error occurred. task.release_resources()
def __init__(self, context, node_id, shared=False, driver_name=None): """Create a new TaskManager. Acquire a lock on a node. The lock can be either shared or exclusive. Shared locks may be used for read-only or non-disruptive actions only, and must be considerate to what other threads may be doing on the same node at the same time. :param context: request context :param node_id: ID or UUID of node to lock. :param shared: Boolean indicating whether to take a shared or exclusive lock. Default: False. :param driver_name: The name of the driver to load, if different from the Node's current driver. :raises: DriverNotFound :raises: NodeNotFound :raises: NodeLocked """ self._dbapi = dbapi.get_instance() self._spawn_method = None self._on_error_method = None self.context = context self.node = None self.shared = shared # NodeLocked exceptions can be annoying. Let's try to alleviate # some of that pain by retrying our lock attempts. The retrying # module expects a wait_fixed value in milliseconds. @retrying.retry( retry_on_exception=lambda e: isinstance(e, exception.NodeLocked), stop_max_attempt_number=CONF.conductor.node_locked_retry_attempts, wait_fixed=CONF.conductor.node_locked_retry_interval * 1000) def reserve_node(): LOG.debug("Attempting to reserve node %(node)s", {'node': node_id}) self.node = self._dbapi.reserve_node(CONF.host, node_id) try: if not self.shared: reserve_node() else: self.node = objects.Node.get(context, node_id) self.ports = self._dbapi.get_ports_by_node_id(self.node.id) self.driver = driver_factory.get_driver(driver_name or self.node.driver) except Exception: with excutils.save_and_reraise_exception(): self.release_resources()
def patch(self, node_uuid, patch): """Update an existing node. :param node_uuid: UUID of a node. :param patch: a json PATCH document to apply to this node. """ if self._from_chassis: raise exception.OperationNotPermitted rpc_node = objects.Node.get_by_uuid(pecan.request.context, node_uuid) # Check if node is transitioning state if rpc_node['target_power_state'] or \ rpc_node['target_provision_state']: msg = _("Node %s can not be updated while a state transition " "is in progress.") raise wsme.exc.ClientSideError(msg % node_uuid, status_code=409) try: node = Node(**jsonpatch.apply_patch(rpc_node.as_dict(), jsonpatch.JsonPatch(patch))) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) # Update only the fields that have changed for field in objects.Node.fields: if rpc_node[field] != getattr(node, field): rpc_node[field] = getattr(node, field) # NOTE(deva): we calculate the rpc topic here in case node.driver # has changed, so that update is sent to the # new conductor, not the old one which may fail to # load the new driver. try: topic = pecan.request.rpcapi.get_topic_for(rpc_node) except exception.NoValidHost as e: # NOTE(deva): convert from 404 to 400 because client can see # list of available drivers and shouldn't request # one that doesn't exist. e.code = 400 raise e try: new_node = pecan.request.rpcapi.update_node( pecan.request.context, rpc_node, topic) except Exception as e: with excutils.save_and_reraise_exception(): LOG.exception(e) return Node.convert_with_links(new_node)
def post(self, port): """Create a new port. :param port: a port within the request body. """ if self._from_nodes: raise exception.OperationNotPermitted try: new_port = pecan.request.dbapi.create_port(port.as_dict()) except Exception as e: with excutils.save_and_reraise_exception(): LOG.exception(e) return Port.convert_with_links(new_port)
def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path, preserve_ephemeral=False): """Create partitions and copy an image to the root partition. :param dev: Path for the device to work on. :param root_mb: Size of the root partition in megabytes. :param swap_mb: Size of the swap partition in megabytes. :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0, no ephemeral partition will be created. :param ephemeral_format: The type of file system to format the ephemeral partition. :param image_path: Path for the instance's disk image. :param preserve_ephemeral: If True, no filesystem is written to the ephemeral block device, preserving whatever content it had (if the partition table has not changed). """ if not is_block_device(dev): raise exception.InstanceDeployFailure(_("Parent device '%s' not found") % dev) part_dict = make_partitions(dev, root_mb, swap_mb, ephemeral_mb) ephemeral_part = part_dict.get('ephemeral') swap_part = part_dict.get('swap') root_part = part_dict.get('root') if not is_block_device(root_part): raise exception.InstanceDeployFailure(_("Root device '%s' not found") % root_part) if swap_part and not is_block_device(swap_part): raise exception.InstanceDeployFailure(_("Swap device '%s' not found") % swap_part) if ephemeral_part and not is_block_device(ephemeral_part): raise exception.InstanceDeployFailure( _("Ephemeral device '%s' not found") % ephemeral_part) dd(image_path, root_part) if swap_part: mkswap(swap_part) if ephemeral_part and not preserve_ephemeral: mkfs_ephemeral(ephemeral_part, ephemeral_format) try: root_uuid = block_uuid(root_part) except processutils.ProcessExecutionError: with excutils.save_and_reraise_exception(): LOG.error(_("Failed to detect root device UUID.")) return root_uuid
def post(self, node): """Create a new node. :param node: a node within the request body. """ if self._from_chassis: raise exception.OperationNotPermitted try: new_node = pecan.request.dbapi.create_node(node.as_dict()) except Exception as e: with excutils.save_and_reraise_exception(): LOG.exception(e) return Node.convert_with_links(new_node)
def __init__(self, context, node_ids, shared=False, driver_name=None): """Create a new TaskManager. Acquire a lock atomically on a non-empty set of nodes. The lock can be either shared or exclusive. Shared locks may be used for read-only or non-disruptive actions only, and must be considerate to what other threads may be doing on the nodes at the same time. :param context: request context :param node_ids: A list of ids or uuids of nodes to lock. :param shared: Boolean indicating whether to take a shared or exclusive lock. Default: False. :param driver_name: The name of the driver to load, if different from the Node's current driver. :raises: DriverNotFound :raises: NodeAlreadyLocked """ self.context = context self.resources = [] self.shared = shared self.dbapi = dbapi.get_instance() self._spawn_method = None # instead of generating an exception, DTRT and convert to a list if not isinstance(node_ids, list): node_ids = [node_ids] locked_node_list = [] try: for id in node_ids: if not self.shared: # NOTE(deva): Only lock one node at a time so we can ensure # that only the right nodes are unlocked. # However, reserve_nodes takes and returns a # list. This should be refactored. node = self.dbapi.reserve_nodes(CONF.host, [id])[0] locked_node_list.append(node.id) else: node = objects.Node.get(context, id) ports = self.dbapi.get_ports_by_node_id(node.id) driver = driver_factory.get_driver(driver_name or node.driver) self.resources.append(NodeResource(node, ports, driver)) except Exception: with excutils.save_and_reraise_exception(): if locked_node_list: self.dbapi.release_nodes(CONF.host, locked_node_list)
def vendor_passthru(self, context, node_id, driver_method, info): """RPC method to encapsulate vendor action. Synchronously validate driver specific info or get driver status, and if successful, start background worker to perform vendor action asynchronously. :param context: an admin context. :param node_id: the id or uuid of a node. :param driver_method: the name of the vendor method. :param info: vendor method args. :raises: InvalidParameterValue if supplied info is not valid. :raises: UnsupportedDriverExtension if current driver does not have vendor interface or method is unsupported. :raises: NoFreeConductorWorker when there is no free worker to start async task. """ LOG.debug(_("RPC vendor_passthru called for node %s.") % node_id) # NOTE(max_lobur): Even though not all vendor_passthru calls may # require an exclusive lock, we need to do so to guarantee that the # state doesn't unexpectedly change between doing a vendor.validate # and vendor.vendor_passthru. task = task_manager.TaskManager(context, node_id, shared=False) try: if not getattr(task.driver, 'vendor', None): raise exception.UnsupportedDriverExtension( driver=task.node.driver, extension='vendor passthru') task.driver.vendor.validate(task, task.node, method=driver_method, **info) # Start requested action in the background. thread = self._spawn_worker(task.driver.vendor.vendor_passthru, task, task.node, method=driver_method, **info) # Release node lock at the end of async action. thread.link(lambda t: task.release_resources()) except Exception: with excutils.save_and_reraise_exception(): # Release node lock if error occurred. task.release_resources()
def _make_password_file(password): """Makes a temporary file that contains the password. :param password: the password :returns: the absolute pathname of the temporary file :raises: Exception from creating or writing to the temporary file """ try: fd, path = tempfile.mkstemp() os.fchmod(fd, stat.S_IRUSR | stat.S_IWUSR) with os.fdopen(fd, "w") as f: f.write(password) yield path utils.delete_if_exists(path) except Exception: with excutils.save_and_reraise_exception(): utils.delete_if_exists(path)
def __iter__(self): """Return a result until we get a 'None' response from consumer""" if self._done: raise StopIteration while True: try: self._iterator.next() except Exception: with excutils.save_and_reraise_exception(): self.done() if self._got_ending: self.done() raise StopIteration result = self._result if isinstance(result, Exception): self.done() raise result yield result
def validate_vendor_action(self, context, node_id, driver_method, info): """Validate driver specific info or get driver status.""" LOG.debug( _("RPC validate_vendor_action called for node %s.") % node_id) with task_manager.acquire(context, node_id, shared=True) as task: try: if getattr(task.driver, 'vendor', None): return task.driver.vendor.validate(task, task.node, method=driver_method, **info) else: raise exception.UnsupportedDriverExtension( driver=task.node.driver, extension='vendor passthru') except Exception as e: with excutils.save_and_reraise_exception(): task.node.last_error = \ _("Failed to validate vendor info. Error: %s") % e task.node.save(context)
def __iter__(self): """Return a result until we get a reply with an 'ending" flag""" if self._done: raise StopIteration while True: try: data = self._dataqueue.get(timeout=self._timeout) result = self._process_data(data) except queue.Empty: self.done() raise rpc_common.Timeout() except Exception: with excutils.save_and_reraise_exception(): self.done() if self._got_ending: self.done() raise StopIteration if isinstance(result, Exception): self.done() raise result yield result