From 61935691030ed12acb3d1bd4448823bcbadd18ff Mon Sep 17 00:00:00 2001 From: Peter Piela Date: Thu, 23 Feb 2017 10:01:49 -0500 Subject: [PATCH] Add support for starting/stopping/accessing the serial console - The action menu in the node-details panel has a new item Enable|Disable console. - The node-details/overview panel has a new "Console info." item in the "General" section. The value of this field is dependent on the console type. For "shellinabox" the value is an anchor with the URL of the console and title "shellinabox". For others, the value is a string representation of the console_info object returned by the get_console api call. Closes-Bug: #1655690 Change-Id: Ibfa687f50d2251987276367d743ffb2a88e52796 --- ironic_ui/api/ironic.py | 36 ++++++++++++++--- ironic_ui/api/ironic_rest_api.py | 28 +++++++++++++ .../dashboard/admin/ironic/ironic.service.js | 40 +++++++++++++++++++ .../node-details/node-details.controller.js | 20 ++++++++++ .../ironic/node-details/node-details.html | 6 +++ .../node-details/sections/overview.html | 10 +++++ .../add-console-support-ccffcedc845ca214.yaml | 15 +++++++ 7 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/add-console-support-ccffcedc845ca214.yaml diff --git a/ironic_ui/api/ironic.py b/ironic_ui/api/ironic.py index e34a1a51..f99c0cc3 100755 --- a/ironic_ui/api/ironic.py +++ b/ironic_ui/api/ironic.py @@ -27,7 +27,6 @@ from openstack_dashboard.api import base DEFAULT_IRONIC_API_VERSION = '1.20' DEFAULT_INSECURE = False DEFAULT_CACERT = None - IRONIC_CLIENT_CLASS_NAME = 'baremetal' @@ -117,6 +116,31 @@ def node_set_provision_state(request, node_id, state, cleansteps=None): cleansteps=cleansteps) +def node_set_console_mode(request, node_id, enabled): + """Start or stop the serial console for a given node. + + :param request: HTTP request. + :param node_id: The UUID or name of the node. + :param enabled: True to start the console, False to stop it + :return: node. + + http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_console_mode + """ + return ironicclient(request).node.set_console_mode(node_id, enabled) + + +def node_get_console(request, node_id): + """Get connection information for a node's console. + + :param request: HTTP request. + :param node_id: The UUID or name of the node. + :return: Console connection information + + http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.get_console + """ + return ironicclient(request).node.get_console(node_id) + + def node_set_maintenance(request, node_id, state, maint_reason=None): """Set the maintenance mode on a given node. @@ -194,7 +218,7 @@ def node_get_boot_device(request, node_id): """Get the boot device for a specified node. :param request: HTTP request. - :param node_id: The id of the node. + :param node_id: The UUID or name of the node. :return: Dictionary with keys "boot_device" and "persistent" http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.get_boot_device @@ -250,16 +274,16 @@ def port_delete(request, port_uuid): return ironicclient(request).port.delete(port_uuid) -def port_update(request, port_id, patch): +def port_update(request, port_uuid, patch): """Update a specified port. :param request: HTTP request. - :param node_id: The uuid of the port. + :param port_id: The UUID of the port. :param patch: Sequence of update operations - :return: port. + :return: Port. http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.port.html#ironicclient.v1.port.PortManager.update """ - port = ironicclient(request).port.update(port_id, patch) + port = ironicclient(request).port.update(port_uuid, patch) return dict([(f, getattr(port, f, '')) for f in res_fields.PORT_DETAILED_RESOURCE.fields]) diff --git a/ironic_ui/api/ironic_rest_api.py b/ironic_ui/api/ironic_rest_api.py index 5fac1f7f..36bde840 100755 --- a/ironic_ui/api/ironic_rest_api.py +++ b/ironic_ui/api/ironic_rest_api.py @@ -177,6 +177,34 @@ class StatesProvision(generic.View): clean_steps) +@urls.register +class StatesConsole(generic.View): + + url_regex = r'ironic/nodes/(?P[0-9a-f-]+)/states/console$' + + @rest_utils.ajax() + def get(self, request, node_uuid): + """Get connection information for the node's console + + :param request: HTTP request. + :param node_id: Node uuid + :return: Connection information + """ + return ironic.node_get_console(request, node_uuid) + + @rest_utils.ajax(data_required=True) + def put(self, request, node_uuid): + """Start or stop the serial console. + + :param request: HTTP request. + :param node_id: Node uuid + :return: Return code + """ + return ironic.node_set_console_mode(request, + node_uuid, + request.DATA.get('enabled')) + + @urls.register class Maintenance(generic.View): diff --git a/ironic_ui/static/dashboard/admin/ironic/ironic.service.js b/ironic_ui/static/dashboard/admin/ironic/ironic.service.js index 454cc37a..80b3ae7d 100755 --- a/ironic_ui/static/dashboard/admin/ironic/ironic.service.js +++ b/ironic_ui/static/dashboard/admin/ironic/ironic.service.js @@ -50,6 +50,8 @@ getNodes: getNodes, getPortsWithNode: getPortsWithNode, getBootDevice: getBootDevice, + nodeGetConsole: nodeGetConsole, + nodeSetConsoleMode: nodeSetConsoleMode, powerOffNode: powerOffNode, powerOnNode: powerOnNode, putNodeInMaintenanceMode: putNodeInMaintenanceMode, @@ -514,6 +516,44 @@ return $q.reject(msg); }); } + + /** + * @description Set the console mode of the node. + * + * http://developer.openstack.org/api-ref/baremetal/? + * expanded=start-stop-console-detail#start-stop-console + * + * @param {string} uuid – UUID of a node. + * @param {boolean} enabled – true to start the console, false to stop it + * @return {promise} Promise + */ + function nodeSetConsoleMode(uuid, enabled) { + return apiService.put('/api/ironic/nodes/' + uuid + '/states/console', + {enabled: enabled}) + .then(function(response) { + var msg = gettext('Refresh page to see updated console details'); + toastService.add('success', interpolate(msg, [uuid], false)); + return response.data; + }) + .catch(function(response) { + var msg = gettext('Unable to set console mode: %s'); + toastService.add('error', interpolate(msg, [response.data], false)); + return $q.reject(msg); + }); + } + + function nodeGetConsole(uuid) { + return apiService.get('/api/ironic/nodes/' + uuid + '/states/console') + .then(function(response) { + return response.data; // Object containing console information + }) + .catch(function(response) { + var msg = gettext('Unable to get console for node %s: %s'); + toastService.add('error', + interpolate(msg, [uuid, response.data], false)); + return $q.reject(msg); + }); + } } }()); diff --git a/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js b/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js index b9f018ec..6950e690 100755 --- a/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js +++ b/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js @@ -72,6 +72,7 @@ ctrl.node = null; ctrl.nodeValidation = []; + ctrl.nodeValidationMap = {}; // Indexed by interface ctrl.nodeStateTransitions = []; ctrl.ports = []; ctrl.portsSrc = []; @@ -84,11 +85,14 @@ ctrl.deletePort = deletePort; ctrl.editPort = editPort; ctrl.refresh = refresh; + ctrl.toggleConsoleMode = toggleConsoleMode; $scope.emptyObject = function(obj) { return angular.isUndefined(obj) || Object.keys(obj).length === 0; }; + $scope.isDefined = angular.isDefined; + var editNodeHandler = $rootScope.$on(ironicEvents.EDIT_NODE_SUCCESS, function() { @@ -149,6 +153,10 @@ return ironic.getNode(uuid).then(function (node) { ctrl.node = node; ctrl.node.id = ctrl.node.uuid; + ironic.nodeGetConsole(uuid).then(function(consoleData) { + ctrl.node.console_enabled = consoleData.console_enabled; + ctrl.node.console_info = consoleData.console_info; + }); }); } @@ -188,9 +196,11 @@ function validateNode() { ironic.validateNode(ctrl.node.uuid).then(function(response) { var nodeValidation = []; + ctrl.nodeValidationMap = {}; angular.forEach(response.data, function(status) { status.id = status.interface; nodeValidation.push(status); + ctrl.nodeValidationMap[status.interface] = status; }); ctrl.nodeValidation = nodeValidation; }); @@ -274,5 +284,15 @@ function refresh() { init(); } + + /** + * @name horizon.dashboard.admin.ironic.NodeDetailsController.toggleConsoleMode + * @description Toggle the state of the console for the current node + * + * @return {void} + */ + function toggleConsoleMode() { + ironic.nodeSetConsoleMode(ctrl.node.uuid, !ctrl.node.console_enabled); + } } })(); diff --git a/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html b/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html index 1f808fc8..0d9cfdc9 100644 --- a/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html +++ b/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html @@ -49,6 +49,12 @@ callback="ctrl.editNode"> {$ ::'Edit' | translate $} + + {$ ctrl.node.console_enabled ? 'Disable console' : 'Enable console' | translate $} + diff --git a/ironic_ui/static/dashboard/admin/ironic/node-details/sections/overview.html b/ironic_ui/static/dashboard/admin/ironic/node-details/sections/overview.html index 0e8cd23a..47476997 100644 --- a/ironic_ui/static/dashboard/admin/ironic/node-details/sections/overview.html +++ b/ironic_ui/static/dashboard/admin/ironic/node-details/sections/overview.html @@ -21,6 +21,16 @@
{$ ctrl.node.reservation | noValue $}
Console Enabled
{$ ctrl.node.console_enabled | yesno $}
+
Console Info
+
+ + shellinabox + +
+
+ {$ ctrl.node.console_info | noValue $} +
diff --git a/releasenotes/notes/add-console-support-ccffcedc845ca214.yaml b/releasenotes/notes/add-console-support-ccffcedc845ca214.yaml new file mode 100644 index 00000000..8786e4d2 --- /dev/null +++ b/releasenotes/notes/add-console-support-ccffcedc845ca214.yaml @@ -0,0 +1,15 @@ +--- +features: + - | + Support has been added for starting, stopping, and accessing the + console associated with a node. + - | + The action dropdown menu in the node-details panel has a new item + ``Enable|Disable console``. + - | + The node-details/overview panel has a new ``Console info`` item in the + ``General`` section. The value of this field is dependent on the console + type. For ``shellinabox`` the value is an anchor with the URL required + to access the web console and title ``shellinabox``. For others, the + value is a string representation of the console_info object returned + by the get_console api call.