Browse Source

Merge "Added support for injecting non-maskable interrupts"

tags/3.1.0
Zuul 1 year ago
parent
commit
77beab6cc4

+ 12
- 0
ironic_ui/api/ironic.py View File

@@ -259,6 +259,18 @@ def node_get_supported_boot_devices(request, node_id):
259 259
     return result.get('supported_boot_devices', [])
260 260
 
261 261
 
262
+def node_inject_nmi(request, node_id):
263
+    """Inject Non-Masking Interrupts into a specified node.
264
+
265
+    :param request: HTTP request.
266
+    :param node_id: The UUID or name of the node.
267
+    :return: Empty response.
268
+
269
+    http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.inject_nmi
270
+    """
271
+    return ironicclient(request).node.inject_nmi(node_id)
272
+
273
+
262 274
 def driver_list(request):
263 275
     """Retrieve a list of drivers.
264 276
 

+ 17
- 0
ironic_ui/api/ironic_rest_api.py View File

@@ -467,3 +467,20 @@ class DriverDetails(generic.View):
467 467
         :return: Dictionary of details
468 468
         """
469 469
         return ironic.driver_details(request, driver_name)
470
+
471
+
472
+@urls.register
473
+class InjectNmi(generic.View):
474
+
475
+    url_regex = r'ironic/nodes/(?P<node_id>{})/management/inject_nmi$'. \
476
+                format(LOGICAL_NAME_PATTERN)
477
+
478
+    @rest_utils.ajax(data_required=True)
479
+    def put(self, request, node_id):
480
+        """Inject Non-Masking Interrupts into a specified node.
481
+
482
+        :param request: HTTP request.
483
+        :param node_id: Node name or uuid.
484
+        :return: Empty response.
485
+        """
486
+        return ironic.node_inject_nmi(request, node_id)

+ 18
- 0
ironic_ui/static/dashboard/admin/ironic/ironic.backend-mock.service.js View File

@@ -662,6 +662,24 @@
662 662
           return [status, null];
663 663
         });
664 664
 
665
+      // Inject NMI
666
+      $httpBackend.whenPUT(/\/api\/ironic\/nodes\/(.+)\/management\/inject_nmi/,
667
+                           undefined,
668
+                           undefined,
669
+                           ['nodeId'])
670
+        .respond(function(method, url, data, headers, params) {
671
+          var status, response;
672
+          if (angular.isDefined(nodes[params.nodeId])) {
673
+            status = responseCode.EMPTY_RESPONSE;
674
+            response = '';
675
+          } else {
676
+            status = responseCode.INTERNAL_SERVER_ERROR;
677
+            response = 'Node ' + params.nodeId +
678
+              ' could not be found. (HTTP 404)';
679
+          }
680
+          return [status, response];
681
+        });
682
+
665 683
       // Validate the interfaces associated with a specified node
666 684
       $httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/validate$/,
667 685
                            undefined,

+ 21
- 0
ironic_ui/static/dashboard/admin/ironic/ironic.service.js View File

@@ -53,6 +53,7 @@
53 53
       getPortsWithNode: getPortsWithNode,
54 54
       getBootDevice: getBootDevice,
55 55
       getSupportedBootDevices: getSupportedBootDevices,
56
+      injectNmi: injectNmi,
56 57
       nodeGetConsole: nodeGetConsole,
57 58
       nodeSetConsoleMode: nodeSetConsoleMode,
58 59
       nodeSetMaintenance: nodeSetMaintenance,
@@ -354,6 +355,26 @@
354 355
         });
355 356
     }
356 357
 
358
+    /**
359
+     * @description Inject Non-Masking Interrupts into a specified node
360
+     *
361
+     * http://developer.openstack.org/api-ref/baremetal/#inject-nmi
362
+     *
363
+     * @param {string} nodeId – UUID or logical name of a node.
364
+     * @return {promise} Promise. No content on success.
365
+     */
366
+    function injectNmi(nodeId) {
367
+      return apiService.put('/api/ironic/nodes/' + nodeId +
368
+                            '/management/inject_nmi')
369
+        .catch(function(response) {
370
+          var msg = interpolate(gettext('Unable to inject NMI: %s'),
371
+                                [response.data],
372
+                                false);
373
+          toastService.add('error', msg);
374
+          return $q.reject(msg);
375
+        });
376
+    }
377
+
357 378
     /**
358 379
      * @description Create an Ironic node
359 380
      *

+ 21
- 0
ironic_ui/static/dashboard/admin/ironic/ironic.service.spec.js View File

@@ -34,6 +34,7 @@
34 34
     'getPortsWithNode',
35 35
     'getBootDevice',
36 36
     'getSupportedBootDevices',
37
+    'injectNmi',
37 38
     'nodeGetConsole',
38 39
     'nodeSetBootDevice',
39 40
     'nodeSetConsoleMode',
@@ -662,6 +663,26 @@
662 663
           ironicBackendMockService.flush();
663 664
         });
664 665
 
666
+        it('injectNmi', function() {
667
+          createNode({driver: defaultDriver})
668
+            .then(function(node) {
669
+              return ironicAPI.injectNmi(node.uuid);
670
+            })
671
+            .then(function(response) {
672
+              expect(response.status).toBe(204);
673
+              expect(response.data).toBe('');
674
+            })
675
+            .catch(failTest);
676
+
677
+          ironicBackendMockService.flush();
678
+        });
679
+
680
+        it('injectNmi - nonexistent node', function() {
681
+          ironicAPI.injectNmi(0)
682
+            .then(failTest);
683
+
684
+          ironicBackendMockService.flush();
685
+        });
665 686
       });
666 687
     });
667 688
 })();

+ 11
- 0
ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js View File

@@ -104,6 +104,7 @@
104 104
     ctrl.refresh = refresh;
105 105
     ctrl.toggleConsoleMode = toggleConsoleMode;
106 106
     ctrl.deletePortgroups = deletePortgroups;
107
+    ctrl.injectNmi = injectNmi;
107 108
 
108 109
     $scope.emptyObject = function(obj) {
109 110
       return angular.isUndefined(obj) || Object.keys(obj).length === 0;
@@ -353,5 +354,15 @@
353 354
         ctrl.refresh();
354 355
       });
355 356
     }
357
+
358
+    /**
359
+     * @name horizon.dashboard.admin.ironic.NodeDetailsController.injectNmi
360
+     * @description Inject non-masking interrupts into the current node
361
+     *
362
+     * @return {void}
363
+     */
364
+    function injectNmi() {
365
+      ironic.injectNmi(ctrl.node.uuid);
366
+    }
356 367
   }
357 368
 })();

+ 13
- 0
ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.spec.js View File

@@ -255,5 +255,18 @@
255 255
       expect(ctrl.node['' + interfaceName + '_interface']).toBeDefined();
256 256
       expect(ctrl.nodeValidation[0].hw_interface).toEqual(hwInterface);
257 257
     });
258
+
259
+    it('should have injectNmi', function () {
260
+      var ctrl;
261
+      createNode()
262
+        .then(function(node) {
263
+          ctrl = createController(node);
264
+        })
265
+        .catch(function() {
266
+          fail();
267
+        });
268
+      ironicBackendMockService.flush();
269
+      expect(ctrl.injectNmi).toBeDefined();
270
+    });
258 271
   });
259 272
 })();

+ 8
- 0
ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html View File

@@ -70,6 +70,14 @@
70 70
                 callback="ctrl.toggleConsoleMode">
71 71
           {$ ctrl.node.console_enabled ? 'Disable console' : 'Enable console' | translate $}
72 72
         </action>
73
+        <li role="presentation">
74
+          <a role="menuitem"
75
+             ng-click="ctrl.injectNmi();
76
+                       $event.stopPropagation();
77
+                       $event.preventDefault()">
78
+            <span>{$ "Inject NMI" | translate $}</span>
79
+          </a>
80
+        </li>
73 81
       </menu>
74 82
     </action-list>
75 83
   </div>

+ 6
- 0
releasenotes/notes/inject-nmi-0320453eaf1bda9b.yaml View File

@@ -0,0 +1,6 @@
1
+---
2
+features:
3
+  - |
4
+    Adds support for injecting non-maskable interrupts into a node. A new
5
+    action ``Inject NMI`` has been added to the node actions dropdown menu
6
+    in the ``Node Details`` page.

Loading…
Cancel
Save