Extend the fake systems driver with external systems status notifier
The fake driver can now share the status of fake systems with any external component expected to run on the VMs. This update adds a function to notify an external API when the power status of a fake VM changes. The notification is sent via a PUT API method with the system object as a JSON parameter. This allows the receiver to implement an endpoint that triggers some process based on the new status. Change-Id: I8897c4ce03fc02b3fec4152ff6d52a4e8e81013a Signed-off-by: Mohammed Boukhalfa <mohammed.boukhalfa@est.tech>
This commit is contained in:
@@ -26,7 +26,8 @@ Systems resource
|
||||
|
||||
For *Systems* resource, emulator maintains two drivers relying on
|
||||
a virtualization backend to emulate bare metal machines by means of
|
||||
virtual machines.
|
||||
virtual machines. In addition, there is a fake driver used to mock
|
||||
bare metal machines.
|
||||
|
||||
The following sections will explain how to configure and use
|
||||
each of these drivers.
|
||||
@@ -388,6 +389,39 @@ the ``fake`` system backend, all operations just return success. Any
|
||||
modifications are done purely in the local cache. This way, many Ironic
|
||||
operations can be tested at scale without access to a large computing pool.
|
||||
|
||||
System status notifications
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``fake`` driver may need to simulate components that run on the VMs to test
|
||||
an end-to-end deployment. This requires a hook interface to integrate external
|
||||
components. For instance, when testing Ironic scalability, Ironic needs to
|
||||
communicate with the Ironic Python Agent (IPA). A fake IPA can be implemented
|
||||
and synchronized with the VM status using this hook, which notifies the fake
|
||||
IPA whenever the VM status changes.
|
||||
|
||||
To enable notifications, set ``external_notifier`` to ``True`` in the fake system
|
||||
object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{
|
||||
"uuid": "7946b59-9e44-4fa7-8e91-f3527a1ef094",
|
||||
"name": "fake",
|
||||
"power_state": "Off",
|
||||
"external_notifier": True,
|
||||
"nics": [
|
||||
{
|
||||
"mac": "00:5c:52:31:3a:9c",
|
||||
"ip": "172.22.0.100"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
After this, whenever the fake driver updates this system object, it will send
|
||||
an HTTP ``PUT`` request with the new system object as ``JSON`` data. The
|
||||
endpoint URL can be configured with the parameter
|
||||
``EXTERNAL_NOTIFICATION_URL``.
|
||||
|
||||
Filtering by allowed instances
|
||||
++++++++++++++++++++++++++++++
|
||||
|
||||
|
||||
@@ -14,11 +14,12 @@ import copy
|
||||
import random
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
from sushy_tools.emulator import memoize
|
||||
from sushy_tools.emulator.resources.systems.base import AbstractSystemsDriver
|
||||
from sushy_tools import error
|
||||
|
||||
|
||||
DEFAULT_UUID = '27946b59-9e44-4fa7-8e91-f3527a1ef094'
|
||||
|
||||
|
||||
@@ -32,11 +33,16 @@ class FakeDriver(AbstractSystemsDriver):
|
||||
'uuid': DEFAULT_UUID,
|
||||
'name': 'fake',
|
||||
'power_state': 'Off',
|
||||
'external_notifier': False,
|
||||
'nics': [
|
||||
{'address': '00:5c:52:31:3a:9c'}
|
||||
{
|
||||
'mac': '00:5c:52:31:3a:9c',
|
||||
'ip': '172.22.0.100'
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
config.setdefault('EXTERNAL_NOTIFICATION_URL', 'http://localhost:9999')
|
||||
cls._config = config
|
||||
cls._logger = logger
|
||||
return cls
|
||||
@@ -61,6 +67,8 @@ class FakeDriver(AbstractSystemsDriver):
|
||||
def _update_if_needed(self, system):
|
||||
pending_power = system.get('pending_power')
|
||||
if pending_power and time.time() >= pending_power['apply_time']:
|
||||
if 'Restart' in pending_power['power_state']:
|
||||
pending_power['power_state'] = 'On'
|
||||
self._update(system,
|
||||
power_state=pending_power['power_state'],
|
||||
pending_power=None)
|
||||
@@ -87,6 +95,8 @@ class FakeDriver(AbstractSystemsDriver):
|
||||
system = self._get(system)
|
||||
system.update(changes)
|
||||
self._systems[system['uuid']] = system
|
||||
if system.get('external_notifier'):
|
||||
self._send_external_notification(system)
|
||||
|
||||
@property
|
||||
def driver(self):
|
||||
@@ -123,7 +133,7 @@ class FakeDriver(AbstractSystemsDriver):
|
||||
pending_state = 'Off'
|
||||
elif 'Restart' in state:
|
||||
system['power_state'] = 'Off'
|
||||
pending_state = 'On'
|
||||
pending_state = state
|
||||
else:
|
||||
raise error.NotSupportedError(
|
||||
f'Power state {state} is not supported')
|
||||
@@ -165,5 +175,36 @@ class FakeDriver(AbstractSystemsDriver):
|
||||
|
||||
def get_nics(self, identity):
|
||||
nics = self._get(identity)['nics']
|
||||
return [{'id': nic.get('address'), 'mac': nic.get('address')}
|
||||
return [{'id': nic.get('mac'), 'mac': nic.get('mac')}
|
||||
for nic in nics]
|
||||
|
||||
def _send_external_notification(self, system):
|
||||
"""Notify external API about a given system changes.
|
||||
|
||||
Args:
|
||||
system (dict): The system dictionary containing system details.
|
||||
|
||||
Logs:
|
||||
Info: Logs the start of the fake IPA boot process.
|
||||
Error: Logs any errors encountered during the request.
|
||||
"""
|
||||
external_notification_url = self._config['EXTERNAL_NOTIFICATION_URL']
|
||||
self._logger.info(
|
||||
'External notification to (%s): node %s power state changes',
|
||||
external_notification_url, system.get('name'))
|
||||
resp = requests.put(
|
||||
external_notification_url, json=system,
|
||||
headers={'Content-type': 'application/json'})
|
||||
|
||||
# Check if the request was unsuccessful
|
||||
if resp.status_code >= 400:
|
||||
self._logger.error(
|
||||
'External notifcation to (%s) about system %s request'
|
||||
'error %d: %s',
|
||||
external_notification_url, system.get('name'),
|
||||
resp.status_code, resp.text)
|
||||
return
|
||||
|
||||
# Log successful notification
|
||||
self._logger.info("External notifcation to (%s) sent about %s",
|
||||
external_notification_url, system.get('name'))
|
||||
|
||||
Reference in New Issue
Block a user