# 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 keystoneauth1 import exceptions as kaexception from oslo_log import log from ironic.common import keystone from ironic.common import states from ironic.conf import CONF LOG = log.getLogger(__name__) NOVA_API_VERSION = "2.1" NOVA_API_MICROVERSION = '2.76' _NOVA_ADAPTER = None def _get_nova_adapter(): global _NOVA_ADAPTER if not _NOVA_ADAPTER: _NOVA_ADAPTER = keystone.get_adapter( 'nova', session=keystone.get_session('nova'), auth=keystone.get_auth('nova'), version=NOVA_API_VERSION) return _NOVA_ADAPTER def _get_power_update_event(server_uuid, target_power_state): return {'name': 'power-update', 'server_uuid': server_uuid, 'tag': target_power_state} def _send_event(context, event, api_version=None): """Sends an event to Nova conveying power state change. :param context: request context, instance of ironic.common.context.RequestContext :param event: A "power-update" event for nova to act upon. :param api_version: api version of nova :returns: A boolean which indicates if the event was sent and received successfully. """ try: nova = _get_nova_adapter() response = nova.post( '/os-server-external-events', json={'events': [event]}, microversion=api_version, global_request_id=context.global_id, raise_exc=False) except kaexception.ClientException as ex: LOG.warning('Could not connect to Nova to send a power notification, ' 'please check configuration. %s', ex) return False try: if response.status_code >= 400: LOG.warning('Failed to notify nova on event: %s. %s.', event, response.text) return False resp_event = response.json()['events'][0] code = resp_event['code'] except Exception as e: LOG.error('Invalid response %s returned from nova for power-update ' 'event %s. %s.', response, event, e) return False if code >= 400: LOG.warning('Nova event: %s returned with failed status.', resp_event) return False LOG.debug('Nova event response: %s.', resp_event) return True def power_update(context, server_uuid, target_power_state): """Creates and sends power state change for the provided server_uuid. :param context: request context, instance of ironic.common.context.RequestContext :param server_uuid: The uuid of the node whose power state changed. :param target_power_state: Targeted power state change i.e "POWER_ON" or "POWER_OFF" :returns: A boolean which indicates if the power update was executed successfully (mainly for testing purposes). """ if not CONF.nova.send_power_notifications: return False if target_power_state == states.POWER_ON: target_power_state = "POWER_ON" elif target_power_state == states.POWER_OFF: target_power_state = "POWER_OFF" else: LOG.error('Invalid Power State %s.', target_power_state) return False event = _get_power_update_event(server_uuid, target_power_state) result = _send_event(context, event, api_version=NOVA_API_MICROVERSION) return result