|
4 | 4 | from contextlib import contextmanager
|
5 | 5 | from functools import cached_property
|
6 | 6 |
|
| 7 | +from cinder import context |
7 | 8 | from cinder import exception
|
8 | 9 | from cinder import interface
|
9 | 10 | from cinder.volume import configuration
|
|
19 | 20 | from cinder.volume.drivers.netapp.dataontap.utils import capabilities
|
20 | 21 | from oslo_config import cfg
|
21 | 22 | from oslo_log import log as logging
|
| 23 | +from oslo_service import loopingcall |
22 | 24 |
|
23 | 25 | LOG = logging.getLogger(__name__)
|
24 | 26 | CONF = cfg.CONF
|
|
33 | 35 | "the driver to dynamically select different SVMs based on the "
|
34 | 36 | "volume's project/tenant ID instead of being confined to one SVM.",
|
35 | 37 | ),
|
| 38 | + cfg.IntOpt( |
| 39 | + "netapp_svm_discovery_interval", |
| 40 | + default=300, |
| 41 | + help="In seconds for SVM discovery. The driver will " |
| 42 | + "periodically scan the NetApp cluster for new SVMs matching the " |
| 43 | + "configured prefix.", |
| 44 | + ), |
36 | 45 | ]
|
37 | 46 |
|
38 | 47 | # Configuration options for dynamic NetApp driver
|
@@ -144,6 +153,8 @@ def __init__(self, *args, **kwargs):
|
144 | 153 | self._libraries = {}
|
145 | 154 | # aggregated stats
|
146 | 155 | self._stats = self._empty_volume_stats()
|
| 156 | + # looping call placeholder |
| 157 | + self._looping_call = None |
147 | 158 |
|
148 | 159 | def _create_svm_lib(self, svm_name: str) -> NetAppMinimalLibrary:
|
149 | 160 | # we create a configuration object per SVM library to
|
@@ -222,12 +233,76 @@ def do_setup(self, ctxt):
|
222 | 233 | svm_lib.do_setup(ctxt)
|
223 | 234 | self._libraries[svm_name] = svm_lib
|
224 | 235 |
|
| 236 | + def _remove_svm_lib(self, svm_name: str, svm_lib: NetAppMinimalLibrary): |
| 237 | + """Remove resources for a given SVM library.""" |
| 238 | + # TODO: Need to free up resources here. |
| 239 | + LOG.info("Removing resources for SVM library %s", svm_name) |
| 240 | + # Stop any looping calls if they exist |
| 241 | + if svm_lib._looping_call and hasattr(svm_lib, "loopingcalls"): |
| 242 | + LOG.info("Stopping looping call for SVM library %s", svm_name) |
| 243 | + svm_lib.loopingcalls.stop_tasks() |
| 244 | + # Adding None coz it has a reference to the looping call |
| 245 | + svm_lib.looping_call = None |
| 246 | + # There are other attributes which are in svm_lib even after |
| 247 | + # Stopping the looping. |
| 248 | + |
| 249 | + def _refresh_svm_libraries(self): |
| 250 | + return self._actual_refresh_svm_libraries(context.get_admin_context()) |
| 251 | + |
| 252 | + def _actual_refresh_svm_libraries(self, ctxt): |
| 253 | + """Refresh the SVM libraries.""" |
| 254 | + LOG.info("Start refreshing SVM libraries") |
| 255 | + # Print all current library keys |
| 256 | + existing_libs = set(self._libraries.keys()) |
| 257 | + LOG.info("Existing library keys: %s", existing_libs) |
| 258 | + # Get the current SVMs from cluster |
| 259 | + current_svms = set(self._get_svms()) |
| 260 | + LOG.info("Current SVMs detected from cluster: %s", current_svms) |
| 261 | + # Remove libraries for SVMs that no longer exist |
| 262 | + stale_svms = existing_libs - current_svms |
| 263 | + for svm_name in stale_svms: |
| 264 | + LOG.info("Removing stale NVMe library for SVM: %s", svm_name) |
| 265 | + # TODO : stop looping calls, free resources. |
| 266 | + svm_lib = self._libraries.get(svm_name) |
| 267 | + self._remove_svm_lib(svm_name, svm_lib) |
| 268 | + del self._libraries[svm_name] |
| 269 | + |
| 270 | + # Add new SVM libraries |
| 271 | + new_svms = current_svms - existing_libs |
| 272 | + for svm_name in new_svms: |
| 273 | + LOG.info("Creating NVMe library for new SVM: %s", svm_name) |
| 274 | + try: |
| 275 | + lib = self._create_svm_lib(svm_name) |
| 276 | + # Call do_setup to initialize the library |
| 277 | + lib.do_setup(ctxt) |
| 278 | + lib.check_for_setup_error() |
| 279 | + LOG.info("Library creation success for SVM: %s", svm_name) |
| 280 | + self._libraries[svm_name] = lib |
| 281 | + except Exception: |
| 282 | + LOG.exception( |
| 283 | + "Failed to create library for SVM %s", |
| 284 | + svm_name, |
| 285 | + ) |
| 286 | + LOG.info("Final libraries loaded: %s", list(self._libraries.keys())) |
| 287 | + |
225 | 288 | def check_for_setup_error(self):
|
226 | 289 | """Check for setup errors."""
|
227 | 290 | for svm_name, svm_lib in self._libraries.items():
|
228 | 291 | LOG.info("Checking NVMe library for errors for SVM %s", svm_name)
|
229 | 292 | svm_lib.check_for_setup_error()
|
230 | 293 |
|
| 294 | + # looping call to refresh SVM libraries |
| 295 | + if not self._looping_call: |
| 296 | + interval = self.configuration.safe_get("netapp_svm_discovery_interval") |
| 297 | + if interval and interval > 0: |
| 298 | + self._looping_call = loopingcall.FixedIntervalLoopingCall( |
| 299 | + self._refresh_svm_libraries |
| 300 | + ) |
| 301 | + # removed initial_delay the first call run after full interval . |
| 302 | + self._looping_call.start(interval=interval) |
| 303 | + else: |
| 304 | + LOG.info("SVM discovery timer disabled (interval=%s)", interval) |
| 305 | + |
231 | 306 | def _svmify_pool(self, pool: dict, svm_name: str, **kwargs) -> dict:
|
232 | 307 | """Applies SVM info to a pool so we can target it and track it."""
|
233 | 308 | # We need to prefix our pool_name, which is 1:1 with the FlexVol
|
|
0 commit comments