Simon Hunt
Committed by Gerrit Code Review

ONOS-2876 -- GUI - Setting friendly device names.

Change-Id: I3e84f473af4c26c9a5e8b5cf9598d1448c4be20c
......@@ -33,6 +33,8 @@ import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.table.TableModel;
import org.onosproject.ui.table.TableRequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
......@@ -61,6 +63,8 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
private static final String DEV_NAME_CHANGE_REQ = "deviceNameChangeRequest";
private static final String DEV_NAME_CHANGE_RESP = "deviceNameChangeResponse";
private static final String ZERO_URI = "of:0000000000000000";
private static final String ID = "id";
private static final String TYPE = "type";
private static final String AVAILABLE = "available";
......@@ -80,17 +84,21 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
private static final String ENABLED = "enabled";
private static final String SPEED = "speed";
private static final String NAME = "name";
private static final String WARN = "warn";
private static final String[] COL_IDS = {
AVAILABLE, AVAILABLE_IID, TYPE_IID, ID,
NUM_PORTS, MASTER_ID, MFR, HW, SW,
AVAILABLE, AVAILABLE_IID, TYPE_IID,
NAME, ID, MASTER_ID, NUM_PORTS, MFR, HW, SW,
PROTOCOL, CHASSIS_ID, SERIAL
};
private static final String ICON_ID_ONLINE = "active";
private static final String ICON_ID_OFFLINE = "inactive";
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
......@@ -100,6 +108,17 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
);
}
// Get friendly name of the device from the annotations
private static String deviceName(Device device) {
String name = device.annotations().value(AnnotationKeys.NAME);
return isNullOrEmpty(name) ? device.id().toString() : name;
}
private static String deviceProtocol(Device device) {
String protocol = device.annotations().value(PROTOCOL);
return protocol != null ? protocol : "";
}
private static String getTypeIconId(Device d) {
return DEV_ICON_PREFIX + d.type().toString();
}
......@@ -130,16 +149,15 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
boolean available = ds.isAvailable(id);
String iconId = available ? ICON_ID_ONLINE : ICON_ID_OFFLINE;
String protocol = dev.annotations().value(PROTOCOL);
row.cell(ID, id)
.cell(NAME, deviceName(dev))
.cell(AVAILABLE, available)
.cell(AVAILABLE_IID, iconId)
.cell(TYPE_IID, getTypeIconId(dev))
.cell(MFR, dev.manufacturer())
.cell(HW, dev.hwVersion())
.cell(SW, dev.swVersion())
.cell(PROTOCOL, protocol != null ? protocol : "")
.cell(PROTOCOL, deviceProtocol(dev))
.cell(NUM_PORTS, ds.getPorts(id).size())
.cell(MASTER_ID, ms.getMasterFor(id));
}
......@@ -153,20 +171,16 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
@Override
public void process(long sid, ObjectNode payload) {
String id = string(payload, "id", "of:0000000000000000");
String id = string(payload, ID, ZERO_URI);
DeviceId deviceId = deviceId(id);
DeviceService service = get(DeviceService.class);
MastershipService ms = get(MastershipService.class);
Device device = service.getDevice(deviceId);
ObjectNode data = MAPPER.createObjectNode();
ObjectNode data = objectNode();
data.put(ID, deviceId.toString());
// Get friendly name of the device from the annotations
String name = device.annotations().value(AnnotationKeys.NAME);
data.put(NAME, isNullOrEmpty(name) ? deviceId.toString() : name);
data.put(NAME, deviceName(device));
data.put(TYPE, capitalizeFully(device.type().toString()));
data.put(TYPE_IID, getTypeIconId(device));
data.put(MFR, device.manufacturer());
......@@ -175,9 +189,9 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
data.put(SERIAL, device.serialNumber());
data.put(CHASSIS_ID, device.chassisId().toString());
data.put(MASTER_ID, ms.getMasterFor(deviceId).toString());
data.put(PROTOCOL, device.annotations().value(PROTOCOL));
data.put(PROTOCOL, deviceProtocol(device));
ArrayNode ports = MAPPER.createArrayNode();
ArrayNode ports = arrayNode();
List<Port> portList = new ArrayList<>(service.getPorts(deviceId));
Collections.sort(portList, (p1, p2) -> {
......@@ -190,13 +204,13 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
}
data.set(PORTS, ports);
ObjectNode rootNode = MAPPER.createObjectNode();
ObjectNode rootNode = objectNode();
rootNode.set(DETAILS, data);
sendMessage(DEV_DETAILS_RESP, 0, rootNode);
}
private ObjectNode portData(Port p, DeviceId id) {
ObjectNode port = MAPPER.createObjectNode();
ObjectNode port = objectNode();
LinkService ls = get(LinkService.class);
String name = p.annotations().value(AnnotationKeys.PORT_NAME);
......@@ -221,6 +235,7 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
}
}
// handler for changing device friendly name
private final class NameChangeHandler extends RequestHandler {
private NameChangeHandler() {
......@@ -229,13 +244,17 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
@Override
public void process(long sid, ObjectNode payload) {
DeviceId deviceId = deviceId(string(payload, "id", "of:0000000000000000"));
DeviceId deviceId = deviceId(string(payload, ID, ZERO_URI));
String name = emptyToNull(string(payload, NAME, null));
log.debug("Name change request: {} -- '{}'", deviceId, name);
NetworkConfigService service = get(NetworkConfigService.class);
BasicDeviceConfig cfg = service.getConfig(deviceId, BasicDeviceConfig.class);
BasicDeviceConfig cfg =
service.addConfig(deviceId, BasicDeviceConfig.class);
// Name attribute missing (or being empty) from the payload means
// that the friendly name should be unset.
cfg.name(emptyToNull(string(payload, "name", null)));
// Name attribute missing from the payload (or empty string)
// means that the friendly name should be unset.
cfg.name(name);
cfg.apply();
sendMessage(DEV_NAME_CHANGE_RESP, 0, payload);
}
......
......@@ -25,6 +25,7 @@
// internal state
var enabled = true,
globalEnabled = true,
keyHandler = {
globalKeys: {},
maskedKeys: {},
......@@ -116,6 +117,9 @@
}
function quickHelp(view, key, code, ev) {
if (!globalEnabled) {
return false;
}
qhs.showQuickHelp(keyHandler);
return true;
}
......@@ -126,6 +130,9 @@
}
function toggleTheme(view, key, code, ev) {
if (!globalEnabled) {
return false;
}
ts.toggleTheme();
return true;
}
......@@ -226,6 +233,9 @@
enableKeys: function (b) {
enabled = b;
},
enableGlobalKeys: function (b) {
globalEnabled = b;
},
checkNotGlobal: checkNotGlobal
};
}]);
......
......@@ -75,30 +75,15 @@
margin: 8px 0;
}
#device-details-panel .name-div {
height: 20px;
padding: 8px 0 0 8px;
}
#device-details-panel .name-div span {
display: inline-block;
}
#device-details-panel .name-div .label {
font-style: italic;
padding-right: 12px;
/* works for both light and dark themes ... */
color: #777;
}
#device-details-panel .name-div .value {
}
#device-details-panel .editable {
cursor: pointer;
border-bottom: 1px dashed darkgreen;
}
#device-details-panel h2 input {
font-size: 1.0em;
}
#device-details-panel .top div.left {
float: left;
padding: 0 18px 0 0;
......
......@@ -35,13 +35,14 @@
<tr>
<td colId="available" class="table-icon" sortable></td>
<td colId="type" class="table-icon" sortable></td>
<td colId="name" sortable>Friendly Name </td>
<td colId="id" sortable>Device ID </td>
<td colId="masterid" sortable>Master Instance </td>
<td colId="num_ports" sortable>Ports </td>
<td colId="num_ports" col-width="60px" sortable>Ports </td>
<td colId="mfr" sortable>Vendor </td>
<td colId="hw" sortable>H/W Version </td>
<td colId="sw" sortable>S/W Version </td>
<td colId="protocol" sortable>Protocol </td>
<td colId="protocol" col-width="80px" sortable>Protocol </td>
</tr>
</table>
</div>
......@@ -64,6 +65,7 @@
<td class="table-icon">
<div icon icon-id="{{dev._iconid_type}}"></div>
</td>
<td>{{dev.name}}</td>
<td>{{dev.id}}</td>
<td>{{dev.masterid}}</td>
<td>{{dev.num_ports}}</td>
......
......@@ -22,7 +22,7 @@
'use strict';
// injected refs
var $log, $scope, $location, fs, mast, ps, wss, is, ns;
var $log, $scope, $loc, fs, mast, ps, wss, is, ns, ks;
// internal state
var detailsPanel,
......@@ -43,13 +43,15 @@
pName = 'device-details-panel',
detailsReq = 'deviceDetailsRequest',
detailsResp = 'deviceDetailsResponse',
nameChangeReq = 'deviceNameChangeRequest',
nameChangeResp = 'deviceNameChangeResponse',
propOrder = [
'type', 'masterid', 'chassisid',
'id', 'type', 'masterid', 'chassisid',
'mfr', 'hw', 'sw', 'protocol', 'serial'
],
friendlyProps = [
'Type', 'Master ID', 'Chassis ID',
'URI', 'Type', 'Master ID', 'Chassis ID',
'Vendor', 'H/W Version', 'S/W Version', 'Protocol', 'Serial #'
],
portCols = [
......@@ -74,57 +76,52 @@
div.on('click', closePanel);
}
function getNameSpan() {
return top.select('.name-div').select('.value');
}
function nameValid(name) {
// TODO: guard against empty strings etc.
return true;
function exitEditMode(nameH2, name) {
nameH2.html(name);
nameH2.classed('editable', true);
editingName = false;
ks.enableGlobalKeys(true);
}
function editNameSave() {
var span = getNameSpan(),
var nameH2 = top.select('h2'),
id = $scope.panelData.id,
val,
newVal;
if (editingName) {
newVal = span.select('input').property('value');
$log.debug("TODO: Saving name change... to '" + newVal + "'");
if (!nameValid(newVal)) {
return editNameCancel();
}
if (editingName) {
val = nameH2.select('input').property('value').trim();
newVal = val || id;
// TODO: send event to server to set friendly name for device
exitEditMode(nameH2, newVal);
$scope.panelData.name = newVal;
span.html(newVal);
span.classed('editable', true);
editingName = false;
wss.sendEvent(nameChangeReq, { id: id, name: val });
}
}
function editNameCancel() {
var span = getNameSpan();
if (editingName) {
$log.debug("Canceling name change...");
span.html($scope.panelData.name);
span.classed('editable', true);
editingName = false;
exitEditMode(top.select('h2'), $scope.panelData.name);
return true;
}
return false;
}
function editName() {
$log.log('editName() .. editing=' + editingName);
var span = getNameSpan();
var nameH2 = top.select('h2'),
tf, el;
if (!editingName) {
editingName = true;
span.classed('editable', false);
span.html('');
span.append('input').classed('name-input', true)
nameH2.classed('editable', false);
nameH2.html('');
tf = nameH2.append('input').classed('name-input', true)
.attr('type', 'text')
.attr('value', $scope.panelData.name);
el = tf[0][0];
el.focus();
el.select();
editingName = true;
ks.enableGlobalKeys(false);
}
}
......@@ -132,14 +129,6 @@
return editNameCancel() || closePanel();
}
function setUpEditableName(top) {
var div = top.append('div').classed('name-div', true);
div.append('span').classed('label', true);
div.append('span').classed('value editable', true)
.on('click', editName);
}
function setUpPanel() {
var container, closeBtn, tblDiv;
detailsPanel.empty();
......@@ -150,9 +139,7 @@
closeBtn = top.append('div').classed('close-btn', true);
addCloseBtn(closeBtn);
iconDiv = top.append('div').classed('dev-icon', true);
top.append('h2');
setUpEditableName(top);
top.append('h2').classed('editable', true).on('click', editName);
tblDiv = top.append('div').classed('top-tables', true);
tblDiv.append('div').classed('left', true).append('table');
......@@ -184,11 +171,11 @@
.append('tbody');
is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
top.select('h2').html(details.id);
top.select('h2').html(details.name);
propOrder.forEach(function (prop, i) {
// properties are split into two tables
addProp(i < 3 ? leftTbl : rightTbl, i, details[prop]);
addProp(i < 4 ? leftTbl : rightTbl, i, details[prop]);
});
}
......@@ -258,6 +245,13 @@
$scope.$apply();
}
function respNameCb(data) {
if (data.warn) {
$log.warn(data.warn, data.id);
top.select('h2').html(data.id);
}
}
function createDetailsPane() {
detailsPanel = ps.createPanel(pName, {
width: wSize.width,
......@@ -276,21 +270,26 @@
.controller('OvDeviceCtrl',
['$log', '$scope', '$location', 'TableBuilderService', 'FnService',
'MastService', 'PanelService', 'WebSocketService', 'IconService',
'NavService',
'NavService', 'KeyService',
function (_$log_, _$scope_, _$location_,
tbs, _fs_, _mast_, _ps_, _wss_, _is_, _ns_) {
tbs, _fs_, _mast_, _ps_, _wss_, _is_, _ns_, _ks_) {
var params,
handlers = {};
$log = _$log_;
$scope = _$scope_;
$location = _$location_;
$loc = _$location_;
fs = _fs_;
mast = _mast_;
ps = _ps_;
wss = _wss_;
is = _is_;
ns = _ns_;
var params = $location.search(),
handlers = {};
ks = _ks_;
params = $loc.search();
$scope.panelData = {};
$scope.flowTip = 'Show flow view for selected device';
$scope.portTip = 'Show port view for selected device';
......@@ -298,6 +297,7 @@
// details panel handlers
handlers[detailsResp] = respDetailsCb;
handlers[nameChangeResp] = respNameCb;
wss.bindHandlers(handlers);
// query for if a certain device needs to be highlighted
......