Merge "Add support for starting/stopping/accessing the serial console"

This commit is contained in:
Jenkins 2017-05-17 00:58:15 +00:00 committed by Gerrit Code Review
commit 31d3897a20
7 changed files with 149 additions and 6 deletions

View File

@ -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])

View File

@ -177,6 +177,34 @@ class StatesProvision(generic.View):
clean_steps)
@urls.register
class StatesConsole(generic.View):
url_regex = r'ironic/nodes/(?P<node_uuid>[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):

View File

@ -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);
});
}
}
}());

View File

@ -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);
}
}
})();

View File

@ -49,6 +49,12 @@
callback="ctrl.editNode">
{$ ::'Edit' | translate $}
</action>
<action button-type="menu-item"
disabled="isDefined(ctrl.nodeValidationMap.console)
&& ctrl.nodeValidationMap.console.result===false"
callback="ctrl.toggleConsoleMode">
{$ ctrl.node.console_enabled ? 'Disable console' : 'Enable console' | translate $}
</action>
</menu>
</action-list>
</div>

View File

@ -21,6 +21,16 @@
<dd>{$ ctrl.node.reservation | noValue $}</dd>
<dt translate>Console Enabled</dt>
<dd>{$ ctrl.node.console_enabled | yesno $}</dd>
<dt translate>Console Info</dt>
<dd ng-if="ctrl.node.console_info.type==='shellinabox'">
<a href="{$ ctrl.node.console_info.url $}"
title="{$ 'Click link to view console' | translate $}">
shellinabox
</a>
</dd>
<dd ng-if="ctrl.node.console_info.type!=='shellinabox'">
{$ ctrl.node.console_info | noValue $}
</dd>
</dl>
</div>

View File

@ -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.