# Copyright 2016 Huawei Technologies Co.,LTD. # 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 oslo_log import log import oslo_messaging as messaging from oslo_service import periodic_task from nimble.common import exception from nimble.common import flow_utils from nimble.common.i18n import _LE from nimble.common.i18n import _LI from nimble.common import neutron from nimble.conf import CONF from nimble.engine.baremetal import ironic from nimble.engine.baremetal import ironic_states from nimble.engine import base_manager from nimble.engine.flows import create_instance from nimble.engine import status LOG = log.getLogger(__name__) class EngineManager(base_manager.BaseEngineManager): """Nimble Engine manager main class.""" RPC_API_VERSION = '1.0' target = messaging.Target(version=RPC_API_VERSION) def _refresh_cache(self): node_cache = {} nodes = ironic.get_node_list(self.ironicclient, detail=True, maintenance=False, provision_state=ironic_states.AVAILABLE, associated=False, limit=0) for node in nodes: node_cache[node.uuid] = node self.node_cache = node_cache @periodic_task.periodic_task( spacing=CONF.engine.sync_node_resource_interval) def _sync_node_resources(self, context): self._refresh_cache() def _set_instance_obj_error_state(self, context, instance): try: instance.status = status.ERROR instance.save() except exception.InstanceNotFound: LOG.debug('Instance has been destroyed from under us while ' 'trying to set it to ERROR', instance=instance) def _destroy_networks(self, context, instance): LOG.debug("unplug: instance_uuid=%(uuid)s vif=%(network_info)s", {'uuid': instance.uuid, 'network_info': str(instance.network_info)}) ports = instance.network_info.keys() for port in ports: neutron.delete_port(context, port, instance.uuid) ironic_ports = ironic.get_ports_from_node(self.ironicclient, instance.node_uuid, detail=True) for pif in ironic_ports: if 'vif_port_id' in pif.extra: ironic.unplug_vif(self.ironicclient, pif.uuid) def _destroy_instance(self, context, instance): ironic.destroy_node(self.ironicclient, instance.node_uuid) LOG.info(_LI('Successfully destroyed Ironic node %s'), instance.node_uuid) def create_instance(self, context, instance, requested_networks, request_spec=None, filter_properties=None): """Perform a deployment.""" LOG.debug("Starting instance...") if filter_properties is None: filter_properties = {} try: flow_engine = create_instance.get_flow( context, self, instance, requested_networks, request_spec, filter_properties, ) except Exception: msg = _("Create manager instance flow failed.") LOG.exception(msg) raise exception.NimbleException(msg) def _run_flow(): # This code executes create instance flow. If something goes wrong, # flow reverts all job that was done and reraises an exception. # Otherwise, all data that was generated by flow becomes available # in flow engine's storage. with flow_utils.DynamicLogListener(flow_engine, logger=LOG): flow_engine.run() try: _run_flow() except exception.NoValidNode: self._set_instance_obj_error_state(context, instance) LOG.error(_LE("Created instance %s failed, No valid node " "is found with the request spec."), instance.uuid) else: LOG.info(_LI("Created instance %s successfully."), instance.uuid) finally: return instance def delete_instance(self, context, instance): """Delete an instance.""" LOG.debug("Deleting instance...") try: self._destroy_networks(context, instance) self._destroy_instance(context, instance) except Exception: LOG.exception(_LE("Error while trying to clean up " "instance resources."), instance=instance) instance.destroy() def _instance_states(self, context, instance): states = ironic.get_node_states(self.ironicclient, instance.node_uuid) LOG.info(_LI('Successfully get ironic node states: %s'), states) return states.to_dict() def instance_states(self, context, instance): """Get an instance states.""" LOG.debug("get instance states") return self._instance_states(context, instance) def _set_power_state(self, context, instance, state): ironic.set_power_state(self.ironicclient, instance.node_uuid, state) LOG.info(_LI('Successfully set ironic node power state: %s'), state) def set_power_state(self, context, instance, state): """Get an instance states.""" LOG.debug("set power state...") return self._set_power_state(context, instance, state) def get_ironic_node(self, context, instance_uuid, fields): """Get a ironic node.""" node = ironic.get_node_by_instance(self.ironicclient, instance_uuid, fields) return node.to_dict() def get_ironic_node_list(self, context, fields): """Get a ironic node list.""" nodes = ironic.get_node_list(self.ironicclient, associated=True, limit=0, fields=fields) return {'nodes': [node.to_dict() for node in nodes]} def list_availability_zones(self, context): """Get availability zone list.""" azs = set() for node in self.node_cache.values(): az = node.properties.get('availability_zone') if az is not None: azs.add(az) return {'availability_zones': list(azs)}