950acaaf17
When the power update was written [1] to use nova's os-server-external-events API, the internals of that method had a bug (see the related bug). Fortunately, the code was written so it works against the nova side with or without the fix. However, for the sake of propriety, this commit refactors the code to reflect nova's behavior more accurately. Specifically: Previously, it was impossible for nova to respond 207 when the client sent a single event (as ironic does). The code path accounting for that 207 existed, but always returned True ("success") rather than returning False ("failure") if the event code was >=400. Fortunately, the return value is only ever used in unit test, not production code, so it didn't matter. With this commit, the 207 path is handled correctly, such that the method "succeeds" if the event code is <400 (which would never happen in real life) and "fails" if the event code is >=400. [1] I6d105524e1645d9a40dfeae2850c33cf2d110826 Related-Bug: #1855752 Change-Id: I13744175127e9956fb785a9efc82193c333b2bdc
118 lines
3.8 KiB
Python
118 lines
3.8 KiB
Python
# 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
|