
--- ui/js/src/kimchi.api.js | 23 ++++ ui/js/src/kimchi.network.js | 14 +++ ui/js/src/kimchi.network_edit_main.js | 216 ++++++++++++++++++++++++++++++++++ ui/pages/network-edit.html.tmpl | 75 ++++++++++++ ui/pages/tabs/network.html.tmpl | 3 +- 5 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 ui/js/src/kimchi.network_edit_main.js create mode 100644 ui/pages/network-edit.html.tmpl diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js index 3a94631..25dc397 100644 --- a/ui/js/src/kimchi.api.js +++ b/ui/js/src/kimchi.api.js @@ -634,6 +634,17 @@ var kimchi = { }); }, + retrieveNetwork : function(name, suc, err) { + wok.requestJSON({ + url : 'plugins/kimchi/networks/' + encodeURIComponent(name), + type : 'GET', + contentType : 'application/json', + dataType : 'json', + success: suc, + error: err + }); + }, + deleteNetwork : function(name, suc, err) { wok.requestJSON({ url : 'plugins/kimchi/networks/' + encodeURIComponent(name), @@ -647,6 +658,18 @@ var kimchi = { }); }, + updateNetwork : function(name, settings, suc, err) { + wok.requestJSON({ + url : "plugins/kimchi/networks/" + encodeURIComponent(name), + type : 'PUT', + contentType : 'application/json', + data : JSON.stringify(settings), + dataType : 'json', + success: suc, + error: err + }); + }, + listHostPartitions : function(suc, err) { wok.requestJSON({ url : 'plugins/kimchi/host/partitions', diff --git a/ui/js/src/kimchi.network.js b/ui/js/src/kimchi.network.js index d362010..9816646 100644 --- a/ui/js/src/kimchi.network.js +++ b/ui/js/src/kimchi.network.js @@ -90,6 +90,8 @@ kimchi.getNetworkItemHtml = function(network) { startClass : network.state === "up" ? "wok-hide-action-item" : "", stopClass : network.state === "down" ? "wok-hide-action-item" : disable_in_use, stopDisabled : network.in_use ? "disabled" : "", + editClass : network.state === "up" || network.in_use ? "disabled" : "", + editDisabled : network.state === "up" || network.in_use ? "disabled" : "", deleteClass : network.state === "up" || network.in_use ? "disabled" : "", deleteDisabled: network.state === "up" || network.in_use ? "disabled" : "" }); @@ -106,6 +108,8 @@ kimchi.stopNetwork = function(network,menu) { if (!network.in_use) { $("[nwAct='delete']", menu).removeClass("disabled"); $(":first-child", $("[nwAct='delete']", menu)).removeAttr("disabled"); + $("[nwAct='edit']", menu).removeClass("disabled"); + $(":first-child", $("[nwAct='edit']", menu)).removeAttr("disabled"); } $(".network-state", $("#" + wok.escapeStr(network.name))).switchClass("loading", "down"); }, function(err) { @@ -127,6 +131,8 @@ kimchi.addNetworkActions = function(network) { $("[nwAct='start']", menu).addClass("disabled"); $("[nwAct='delete']", menu).addClass("disabled"); $(":first-child", $("[nwAct='delete']", menu)).attr("disabled", true); + $("[nwAct='edit']", menu).addClass("disabled"); + $(":first-child", $("[nwAct='edit']", menu)).attr("disabled", true); kimchi.toggleNetwork(network.name, true, function() { $("[nwAct='start']", menu).addClass("wok-hide-action-item"); $("[nwAct='start']", menu).removeClass("disabled"); @@ -141,8 +147,10 @@ kimchi.addNetworkActions = function(network) { $("[nwAct='start']", menu).removeClass("disabled"); if (!network.in_use) { $("[nwAct='delete']", menu).removeClass("disabled"); + $("[nwAct='edit']", menu).removeClass("disabled"); } $(":first-child", $("[nwAct='delete']", menu)).removeAttr("disabled"); + $(":first-child", $("[nwAct='edit']", menu)).removeAttr("disabled"); wok.message.error(err.responseJSON.reason); }); } else if ($(evt.currentTarget).attr("nwAct") === "stop") { @@ -179,6 +187,12 @@ kimchi.addNetworkActions = function(network) { $('#networkGrid').dataGrid('deleteRow', $(evt.currentTarget).parents(".wok-datagrid-row")); }); }, null); + } else if ($(evt.currentTarget).attr("nwAct") === "edit") { + if (network.state === "up" || network.in_use) { + return false; + } + kimchi.selectedNetwork = network.name; + wok.window.open('plugins/kimchi/network-edit.html'); } }); diff --git a/ui/js/src/kimchi.network_edit_main.js b/ui/js/src/kimchi.network_edit_main.js new file mode 100644 index 0000000..24ae0c5 --- /dev/null +++ b/ui/js/src/kimchi.network_edit_main.js @@ -0,0 +1,216 @@ +/* + * Project Kimchi + * + * Copyright IBM Corp, 2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +kimchi.network_edit_main = function() { + var initNetwork = function(network) { + var networkType = network['connection']; + $('#bridgedContent').hide(); + $('#networkType').val(networkType); + $('#networkName').val(kimchi.selectedNetwork); + + var subnetValue = network['subnet']; + if (subnetValue === "") { + $('#networkSubnetRange').val("unavailable"); + } else { + $('#networkSubnetRange').val(subnetValue); + } + + // Default to hide Subnet + $('#subnetRange').hide(); + + if(networkType === "nat" || networkType === "isolated") { + //Show subnet/dhcp range + $('#subnetRange').show(); + } + + if (networkType === kimchi.NETWORK_TYPE_MACVTAP || networkType === kimchi.NETWORK_TYPE_VEPA || networkType === kimchi.NETWORK_TYPE_BRIDGED) { + $('#bridgedContent').show(); + $('#networkDestination').show(); + $('#vlan').hide(); + if (networkType === kimchi.NETWORK_TYPE_BRIDGED) { + //Now check if there's a vlan id and only show if one exists + var netInterface = network['interfaces']; + var netInterfaceParts = netInterface[0].split('-', 2); + var netvlanID = netInterfaceParts[1]; + if (netvlanID !== undefined) { + //Show vlan ID field; do not show the checkbox + $('#vlan').show(); + $('#vlan_chkbox').hide(); + $('#vlan-enabled').show(); + $('#networkVlanID').val(netvlanID); + } + } + } + + kimchi.setupNetworkFormEventForEdit(network); + + //TODO: Trial and error trying to get select box to show show the one that it's currently set to but it doesn't work + var netInterface = network['interfaces']; + $('#networkDestinationID').append('val', netInterface); + $('#networkDestinationID').val(netInterface); + $("#networkDestinationID option[value='+netInterface+']").prop('selected', true); + $('#networkDestinationID').selectpicker('refresh'); + }; + + kimchi.retrieveNetwork(kimchi.selectedNetwork, initNetwork); + + var generalSubmit = function(event) { + $('#networkFormOk').prop('disabled', true); + var data = $('#networkConfig').serializeObject(); + kimchi.updateNetworkValues(); + }; + + $('#networkConfig').on('submit', generalSubmit); + $('#networkFormOk').on('click', generalSubmit); + +}; + +kimchi.setupNetworkFormEventForEdit = function(network) { + //Not sure if this is needed since not much testing on bridged network has been done + //if (kimchi.capabilities && kimchi.capabilities.nm_running) { + // wok.message.warn(i18n['KCHNET6001W'],'#alert-modal-container'); + //} + + // Netowrk name validation + $("#networkName").on("keyup", function(event) { + $("#networkName").toggleClass("invalid-field", !$("#networkName").val().match(/^[^\"\/]+$/)); + kimchi.updateNetworkFormButtonForEdit(); + }); + + var selectedType = network['connection']; + if(selectedType === kimchi.NETWORK_TYPE_MACVTAP || selectedType === kimchi.NETWORK_TYPE_VEPA) { + if (selectedType === kimchi.NETWORK_TYPE_VEPA){ + $('#networkDestinationID').attr('multiple', true); + if($('#networkDestinationID option').length > 10 ) { + $('#networkDestinationID').data('liveSearch',true); + } + } + else { + $('#networkDestinationID').attr('multiple', false).data('liveSearch',false); + } + $('#networkDestinationID').selectpicker('destroy'); + + kimchi.loadInterfacesForEdit(new Array("nic", "bonding")); + } else { + kimchi.loadInterfacesForEdit(); + } + + //TODO: Need to confirm if this is still needed + var netInterface = network['interfaces']; + $('#networkDestinationID').append('val', netInterface); + $('#networkDestinationID').val(netInterface); + $("#networkDestinationID option[value='+netInterface+']").prop('selected', true); + $('#networkDestinationID').selectpicker('refresh'); +}; + +kimchi.updateNetworkFormButtonForEdit = function() { + if ($("#networkName").hasClass("invalid-field")){ + $('#networkFormOk').prop('disabled', true); + } else{ + $('#networkFormOk').prop('disabled', false); + } +}; + +kimchi.getNetworkDialogValuesForEdit = function() { + var network = { + name : $("#networkName").val(), + type : $("#networkType").val(), + subnetRange: $("#networkSubnetRange").val(), + interface: $("#networkDestinationID").val(), + vlan_id: $("#networkVlanID").val() + }; + + if (network.type === kimchi.NETWORK_TYPE_BRIDGED) { + //TODO: More checking may need to be done here + if (network.vlan_id !== "") { + network.vlan_id = parseInt($("#networkVlanID").val()); + } + } + return network; +}; + +kimchi.updateNetworkValues = function() { + var network = kimchi.getNetworkDialogValuesForEdit(); + var settings = { + name : network.name, + subnet: network.subnetRange, + interfaces: [ network.interface ], + vlan_id: network.vlan_id + }; + + if (network.type !== "nat" && network.type !== "isolated") { + delete settings['subnet']; + } else { // either nat or isolated + delete settings['interfaces']; + delete settings['vlan_id']; + } + + if (network.type === kimchi.NETWORK_TYPE_BRIDGED) { + if (settings.vlan_id === "") { + delete settings['vlan_id']; + } else { + settings.vlan_id = parseInt($("#networkVlanID").val()); + } + } else { + delete settings['vlan_id']; + } + + // Just like in Add Network, VEPA connection - network.interface - is already an array + if (network.type === kimchi.NETWORK_TYPE_VEPA) { + settings.interfaces = network.interface; + } + + kimchi.updateNetwork(kimchi.selectedNetwork, settings, function() { + $("#networkBody").empty(); + kimchi.initNetworkListView(); + wok.window.close(); + }, function(settings) { + wok.message.error(settings.responseJSON.reason,'#alert-modal-container'); + $('#networkFormOk').prop('disabled', false); + }); +}; + +kimchi.loadInterfacesForEdit = function(interfaceFilterArray) { + kimchi.getInterfaces(function(result) { + var options = []; + $selectDestination = $('#networkDestinationID'); + $selectDestination.empty(); + var nics = {}; + $('#networkDestinationID').find('option').remove(); + var selectDestinationOptionHTML = ''; + for (var i = 0; i < result.length; i++) { + if (typeof interfaceFilterArray === 'undefined') { + options.push({label:result[i].name,value:result[i].name}); + nics[result[i].name] = result[i]; + selectDestinationOptionHTML += '<option data-type="'+ result[i].type +'" value="'+ result[i].name + '">' + result[i].name + '</option>'; + } else { + for (var k = 0; k < interfaceFilterArray.length; k++) { + if (result[i].type == interfaceFilterArray[k]) { + options.push({label:result[i].name,value:result[i].name}); + nics[result[i].name] = result[i]; + selectDestinationOptionHTML += '<option data-type="'+ result[i].type +'" value="'+ result[i].name + '">' + result[i].name + '</option>'; + } + } + } + } + + $selectDestination.append(selectDestinationOptionHTML); + $('#networkDestinationID').selectpicker('refresh'); + }); + +}; diff --git a/ui/pages/network-edit.html.tmpl b/ui/pages/network-edit.html.tmpl new file mode 100644 index 0000000..3f2f3fe --- /dev/null +++ b/ui/pages/network-edit.html.tmpl @@ -0,0 +1,75 @@ +#* + * Project Kimchi + * + * Copyright IBM Corp, 2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *# +#unicode UTF-8 +#import gettext +#from wok.cachebust import href +#silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang, fallback=True) +#silent _ = t.gettext +#silent _t = t.gettext +<html> +<body> +<div id="edit-network-window" class="window modal-content"> + <div class="modal-header"> + <h4 class="modal-title" id="networkModalLabel">$_("Edit Network")</h4> + </div> + <div id="networkConfig" class="modal-body"> + <span id="alert-modal-container"></span> + <div class="form-group"> + <label for="networkType">$_("Network Type")</label> + <input type="text" class="form-control" id="networkType" disabled="disabled" /> + </div> + <div class="form-group"> + <label for="networkName">$_("Network Name")</label> + <input type="text" class="form-control" name="netName" id="networkName" /> + <p class="help-block"> + <i class="fa fa-info-circle"></i> $_("Name should not contain '/' and '\"'.")</p> + </div> + <div id="subnetRange" class="form-group" "col-md-3"> + <div class="form-group"> + <label for="networkSubnetRange">$_("Address Space")</label> + <input type="text" class="form-control" id="networkSubnetRange" /> + </div> + </div> + <div id="bridgedContent"> + <div id="networkDestination" class="form-group"> + <label for="networkDestinationID">$_("Destination")</label> + <select id="networkDestinationID" data-size="5" class="selectpicker col-md-12 col-lg-12 form-control"> + </select> + </div> + <div class="form-group" id="vlan"> + <div id="vlan_chkbox"> + <input id="enableVlan" class="wok-checkbox" type="checkbox" value="" /> + <label for="enableVlan" id="labelEnableVlan">$_("Enable VLAN") </label> + </div> + <div id="vlan-enabled"> + <label for="networkVlanID" id="labelNetworkVlanID">$_("VLAN ID"): </label> + <input type="text" id="networkVlanID" class="form-control" /> + </div> + </div> + </div> + </div> + <div class="modal-footer"> + <button type="submit" id="networkFormOk" class="btn btn-default">$_("Save")</button> + <button type="button" id="networkFormCancel" data-dismiss="modal" class="btn btn-default">$_("Cancel")</button> + </div> +</div> +<script type="text/javascript"> +kimchi.network_edit_main(); +</script> +</body> +</html> diff --git a/ui/pages/tabs/network.html.tmpl b/ui/pages/tabs/network.html.tmpl index d8660d3..6ddabaa 100644 --- a/ui/pages/tabs/network.html.tmpl +++ b/ui/pages/tabs/network.html.tmpl @@ -99,6 +99,7 @@ <ul class="dropdown-menu" role="menu"> <li role="presentation" nwAct="start" class='{startClass}'><a><i class="fa fa-undo"></i>$_("Start")</a></li> <li role="presentation" nwAct="stop" class='{stopClass}'><a {stopDisabled}><i class="fa fa-ban"></i>$_("Stop")</a></li> + <li role="presentation" nwAct="edit" class='{editClass}'><a {editDisabled}><i class="fa fa-pencil"></i>$_("Edit")</a></li> <li role="presentation" nwAct="delete" class='critical {deleteClass}'><a {deleteDisabled}><i class="fa fa-minus-circle"></i>$_("Delete")</a></li> </ul> </div> @@ -110,4 +111,4 @@ kimchi.initNetwork(); </script> </body> -</html> \ No newline at end of file +</html> -- 2.5.0