mogan/nimble/engine/manager.py
2016-12-17 00:24:24 +00:00

187 lines
6.9 KiB
Python

# 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)}