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:
Mohammed Boukhalfa
2024-07-01 08:22:52 +03:00
parent 6958f7afef
commit 4268005227
2 changed files with 80 additions and 5 deletions
+35 -1
View File
@@ -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'))