[PATCH v4] [WoK 0/5] 'reload' API implementation
by dhbarboza82@gmail.com
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
v4:
- fixed a '()' in test_api.py
- changed the positioning of the notification message in i18n.py
v3:
- 'add_notification' is now being called before the reload command
v2:
- changed API name to 'reload'
- added test_api tests
This patch set implements a new WOK API called 'reload' under
/config/reload.
To test it:
$ curl -k -u danielhb -H "Content-Type: application/json" -H "Accept: application/json" -X POST 'https://localhost:8001/config/reload' -d'{}'
Enter host password for user 'danielhb':
{
"proxy_port":"8001",
"websockets_port":"64667",
"version":"2.3.0-55.git5c9127b",
"auth":"pam",
"server_root":""
}
This will reload WoK and all its plug-ins.
Daniel Henrique Barboza (5):
reload API: doc changes
reload API: control and model changes
reload API: new file tests/test_config_model.py
reload API: added rest API tests
reload API: adding notification before reloading operation
docs/API/config.md | 2 +-
src/wok/control/config.py | 4 +++-
src/wok/i18n.py | 2 ++
src/wok/model/config.py | 22 +++++++++++++++++++++-
tests/test_api.py | 9 ++++++++-
tests/test_config_model.py | 41 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 76 insertions(+), 4 deletions(-)
create mode 100644 tests/test_config_model.py
--
2.7.4
7 years, 10 months
[PATCH v3] [WoK 0/5] 'reload' API implementation
by dhbarboza82@gmail.com
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
v3:
- 'add_notification' is now being called before the reload command
v2:
- changed API name to 'reload'
- added test_api tests
This patch set implements a new WOK API called 'reload' under
/config/reload.
To test it:
$ curl -k -u danielhb -H "Content-Type: application/json" -H "Accept: application/json" -X POST 'https://localhost:8001/config/reload' -d'{}'
Enter host password for user 'danielhb':
{
"proxy_port":"8001",
"websockets_port":"64667",
"version":"2.3.0-55.git5c9127b",
"auth":"pam",
"server_root":""
}
This will reload WoK and all its plug-ins.
Daniel Henrique Barboza (5):
reload API: doc changes
reload API: control and model changes
reload API: new file tests/test_config_model.py
reload API: added rest API tests
reload API: adding notification before reloading operation
docs/API/config.md | 2 +-
src/wok/control/config.py | 4 +++-
src/wok/i18n.py | 2 ++
src/wok/model/config.py | 22 +++++++++++++++++++++-
tests/test_api.py | 9 ++++++++-
tests/test_config_model.py | 41 +++++++++++++++++++++++++++++++++++++++++
6 files changed, 76 insertions(+), 4 deletions(-)
create mode 100644 tests/test_config_model.py
--
2.7.4
7 years, 10 months
[PATCH v3] [Kimchi 0/5] Live migration RDMA support
by dhbarboza82@gmail.com
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
v3:
- added migrate mockmodel API and test_rest tests
v2:
- added unit test.
This patch set adds Live Migration RDMA support in Kimchi.
The backend/frontend will not verify if the network card
is RDMA capable before doing the migration. User will have
to deal with the libvirt errors in case RDMA is chosen
without proper support/setup.
Daniel Henrique Barboza (5):
Live migration RDMA support: doc changes
Live migration RDMA support: model changes
Live migration RDMA support: ui changes
Live migration RDMA support: test changes
Live migration RDMA support: mockmodel changes
API.json | 5 ++++
control/vms.py | 5 ++--
docs/API.md | 1 +
i18n.py | 3 +-
mockmodel.py | 17 ++++++++++-
model/vms.py | 18 ++++++++---
tests/test_livemigration.py | 53 ++++++++++++++++++++++++++++++++-
tests/test_rest.py | 48 ++++++++++++++++++++++++++++-
ui/js/src/kimchi.guest_livemigration.js | 7 +++--
ui/pages/guest-migration.html.tmpl | 6 +++-
10 files changed, 149 insertions(+), 14 deletions(-)
--
2.7.4
7 years, 10 months
[PATCH][Kimchi] Bug fix #1016: Display current storage volume size on resize dialog
by Ramon Medeiros
Query storage volume size and show before resize
Signed-off-by: Ramon Medeiros <ramonn(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.storagepool_resize_volume_main.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/ui/js/src/kimchi.storagepool_resize_volume_main.js b/ui/js/src/kimchi.storagepool_resize_volume_main.js
index 1f5b4db..6e4ad73 100644
--- a/ui/js/src/kimchi.storagepool_resize_volume_main.js
+++ b/ui/js/src/kimchi.storagepool_resize_volume_main.js
@@ -1,7 +1,7 @@
/*
* Project Kimchi
*
- * Copyright IBM Corp, 2016
+ * Copyright IBM Corp, 2016-2017
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -22,6 +22,11 @@ kimchi.sp_resize_volume_main = function() {
var size = $('#volume-size');
var form = $('form#form-sp-resize-volume');
+ // set storage volume size
+ kimchi.getStoragePoolVolume(kimchi.selectedSP, kimchi.selectedVolumes[0], function(volume) {
+ size.val(volume["capacity"] / (1024 * 1024));
+ });
+
$(addButton).prop('disabled',true);
$(size).on('keyup change', function(){
if($(this).val() > 0) {
--
2.7.4
7 years, 10 months
[PATCH v4] [Kimchi] Issue# 979 - Change boot order UI
by bianca@linux.vnet.ibm.com
From: Bianca Carvalho <bianca(a)linux.vnet.ibm.com>
This patch adds the UI portion for supporting changing the guest boot order
via edit guest panel. This was based off of what Samuel had prototyped.
Issue found in backend during test:
When updating the VM even with just changing the name, the bootorder gets reset to 'hd' only.
I tested this using the curl command and confirmed that indeed it does get reset to one entry only.
Issue written to address this: https://github.com/kimchi-project/kimchi/issues/1012
Signed-off-by: Bianca Carvalho <bianca(a)linux.vnet.ibm.com>
---
ui/css/kimchi.css | 57 ++++++++++++++-
ui/css/src/modules/_edit-guests.scss | 60 +++++++++++++++-
ui/js/src/kimchi.guest_edit_main.js | 133 ++++++++++++++++++++++++++++++++++-
ui/pages/guest-edit.html.tmpl | 37 ++++++++--
4 files changed, 280 insertions(+), 7 deletions(-)
diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css
index fff3279..16cc161 100644
--- a/ui/css/kimchi.css
+++ b/ui/css/kimchi.css
@@ -1,7 +1,7 @@
/*
* Project Kimchi
*
- * Copyright IBM Corp, 2015-2016
+ * Copyright IBM Corp, 2015-2017
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1557,6 +1557,61 @@ body.wok-gallery {
overflow: visible;
}
+ul {
+ cursor: default;
+}
+
+.boot-order {
+ display: block;
+ width: 85%;
+ font-size: 14px;
+ line-height: 1.42857;
+ color: #444;
+ overflow: hidden;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+ -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+ transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+}
+
+.boot-order:focus,
+.boot-order.focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
+}
+
+.boot-order > li {
+ cursor: move;
+ /* fallback if grab cursor is unsupported */
+ cursor: grab;
+ cursor: -moz-grab;
+ cursor: -webkit-grab;
+ border-left: 0;
+ border-right: 0;
+}
+
+.boot-order > li:first-child {
+ border-top: 0;
+}
+
+.boot-order > li:last-child {
+ border-bottom: 0;
+}
+
+.boot-order > li.ui-sortable-helper {
+ cursor: grabbing;
+ cursor: -moz-grabbing;
+ cursor: -webkit-grabbing;
+ border: 1px solid #ccc !important;
+}
+
/* Add Template Modal Window */
.templates-modal .modal-dialog {
width: 1100px;
diff --git a/ui/css/src/modules/_edit-guests.scss b/ui/css/src/modules/_edit-guests.scss
index 25d4d65..c8cc122 100644
--- a/ui/css/src/modules/_edit-guests.scss
+++ b/ui/css/src/modules/_edit-guests.scss
@@ -1,7 +1,7 @@
//
// Project Kimchi
//
-// Copyright IBM Corp, 2015-2016
+// Copyright IBM Corp, 2015-2017
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -429,3 +429,61 @@
#form-guest-storage-add .form-section .field {
overflow: visible;
}
+
+ul {
+ cursor: default;
+}
+
+.boot-order {
+ display: block;
+ width: 85%;
+ font-size: 14px;
+ line-height: 1.42857;
+ color: #444;
+ overflow: hidden;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+ -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+ transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
+}
+
+.boot-order:focus,
+.boot-order.focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
+}
+
+.boot-order > li {
+ cursor: move; /* fallback if grab cursor is unsupported */
+ cursor: grab;
+ cursor: -moz-grab;
+ cursor: -webkit-grab;
+ border-left: 0;
+ border-right: 0;
+}
+
+.boot-order > li:first-child {
+ border-top: 0;
+}
+
+.boot-order > li:last-child {
+ border-bottom: 0;
+}
+
+.boot-order > li.ui-sortable-helper {
+ cursor: grabbing;
+ cursor: -moz-grabbing;
+ cursor: -webkit-grabbing;
+ border: 1px solid #ccc !important;
+}
+
+.boot-order li i {
+ text-align: right;
+}
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 1682a58..2f15729 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -15,6 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
kimchi.guest_edit_main = function() {
var authType;
var formTargetId;
@@ -26,6 +27,7 @@ kimchi.guest_edit_main = function() {
var networkOptions = "";
clearTimeout(kimchi.vmTimeout);
+ var bootOrderOptions = [];
$('#modalWindow').on('hidden.bs.modal', function() {
kimchi.setListVMAutoTimeout();
@@ -45,7 +47,7 @@ kimchi.guest_edit_main = function() {
var submitForm = function(event) {
- // tap map, "general": 0, "storage": 1, "interface": 2, "permission": 3, "password": 4
+ // tap map, "general": 0, "storage": 1, "interface": 2, "permission": 3, "pci": 4, "snapshot": 5, "processor": 6
var submit_map = {
0: generalSubmit,
3: permissionSubmit,
@@ -966,6 +968,113 @@ kimchi.guest_edit_main = function() {
}
};
+ var setupBootOrder = function(guest) {
+ var guestBootOrder = guest['bootorder'];
+ $("#myList").empty();
+ $.each(guestBootOrder, function(index, value) {
+ var itemNode = $.parseHTML("<li class='list-group-item' " + "data-value=" + value + ">" + value + "<button class='btn btn-link deleteDev'><i class='fa fa-minus-circle'></i> Remove</button></li>");
+ $("#myList").append(itemNode);
+ });
+
+ $('.boot-order').sortable({
+ items: 'li',
+ cursor: 'move',
+ opacity: 0.6,
+ containment: "parent",
+ start: function(event, ui) {
+ $(this).addClass('focus');
+ },
+ stop: function(event, ui) {
+ $(this).removeClass('focus');
+ },
+ change: function(event, ui) {
+ // callback once started changing order
+ },
+ update: function(event, ui) {
+ // callback once finished order
+ $(saveButton).prop('disabled', false);
+ bootOrderOptions = [];
+ $("#myList li").each(function() {
+ bootOrderOptions.push($(this).attr("data-value"))
+ });
+ bootOrderOptions.forEach(function(entry) {
+ console.log(entry);
+ });
+ }
+ });
+
+ $(".deleteDev").on('click', function(evt) {
+ evt.preventDefault();
+ var item = $(this).parent().attr("data-value");
+ var index = guestBootOrder.indexOf(item);
+ if (index !== -1) {
+ guestBootOrder.splice(index, 1);
+ }
+ var data = {
+ bootorder: guestBootOrder
+ };
+ kimchi.updateVM(kimchi.selectedGuest, data, function() {
+ wok.window.close();
+ });
+ });
+
+ $("#guest-edit-add-boot-button").on('click', function(evt) {
+ evt.preventDefault();
+ var html1 = "<div class='item' id='bootDevOrder'><span class='cell column-device'><select id='bootDev' data-none-selected-text='Nothing selected'>"
+ var html2 = "</select></span><span class='cell column-actions action-area'><button class='btn btn-primary save'>Save</button> <button class='btn btn-primary cancel'>Remove</button></span><div>"
+ var selectOptions = []
+ var dev = ["cdrom", "hd", "network"];
+ $.each(dev, function(index, value) {
+ if (!($.inArray(value, guestBootOrder) > -1)) {
+ var option = "<option id='"+ value +"-check'>" + value + "</option>"
+ selectOptions.push(option);
+ }
+ });
+ var addBootorder = html1 + selectOptions.toString().replace(",", "") + html2;
+ var selectionVal = $("#add-boot-order").html(addBootorder);
+ addItem({ bootorder: selectionVal });
+
+ if ($('.body:not(:empty)')) {
+ $("#guest-edit-add-boot-button").prop('disabled', true);
+ }
+ });
+
+ if (guestBootOrder.length == 3) {
+ $("#guest-edit-add-boot-button").prop('disabled', true);
+ }
+
+ var addItem = function(data) {
+ var itemNode = $.parseHTML(wok.substitute($('#add-boot-order').html(), data));
+ $(".body", "#form-guest-edit-bootorder").append(itemNode);
+
+ $('#bootDev').selectpicker({
+ size: 4,
+ width: '150px'
+ });
+
+ $(".save", itemNode).on('click', function(evt) {
+ evt.preventDefault();
+ guestBootOrder.push($("select", "#bootDevOrder").val());
+ var data = {
+ bootorder: guestBootOrder
+ };
+
+ kimchi.updateVM(kimchi.selectedGuest, data, function() {
+ wok.window.close();
+ });
+ });
+ $(".cancel", itemNode).on('click', function(evt) {
+ evt.preventDefault();
+ var item = $(this).parent().parent();
+ $("label", item).text() === "" ? item.remove() : toggleEdit(item, false, data.id);
+
+ $("#guest-edit-add-boot-button").prop('disabled', false);
+ });
+
+ };
+
+ };
+
var initContent = function(guest) {
guest['icon'] = guest['icon'] || 'plugins/kimchi/images/icon-vm.png';
$('#form-guest-edit-general').fillWithObject(guest);
@@ -1031,6 +1140,7 @@ kimchi.guest_edit_main = function() {
setupPermission();
setupPCIDevice();
setupSnapshot();
+ setupBootOrder(guest);
kimchi.init_processor_tab(guest.cpu_info, $(saveButton));
if ((kimchi.thisVMState === "running") || (kimchi.thisVMState === "paused")) {
@@ -1128,6 +1238,27 @@ kimchi.guest_edit_main = function() {
});
});
+ //Format the strings to go in the array before passing in to the API
+ //"hd", "network", "cdrom"
+ var formattedBootOrderOptions = [];
+ for (var i=0; i<bootOrderOptions.length; i++) {
+ var str = bootOrderOptions[i].trim();
+ str = str.toLowerCase();
+ if (str === "hdd") {
+ str = "hd";
+ } else if (str === "cd-rom") {
+ str = "cdrom";
+ }
+ formattedBootOrderOptions.push(str);
+ }
+ var data = {
+ bootorder: formattedBootOrderOptions
+ };
+ kimchi.updateVM(kimchi.selectedGuest, data, function() {
+ wok.window.close();
+ }, function(err) {
+ wok.message.error(err.responseJSON.reason,'#alert-modal-container');
+ });
};
var permissionSubmit = function(event) {
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index d8a482c..df35b8e 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -1,7 +1,7 @@
#*
* Project Kimchi
*
- * Copyright IBM Corp, 2013-2016
+ * Copyright IBM Corp, 2013-2017
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@
</div>
<div class="modal-body">
<span id="alert-modal-container"></span>
-<ul class="nav nav-tabs" role="tablist">
+ <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#form-guest-edit-general" aria-controls="form-guest-edit-general" role="tab" data-id="form-guest-edit-general" data-toggle="tab">$_("General")</a></li>
<li role="presentation"><a href="#form-guest-edit-storage" aria-controls="form-guest-edit-storage" role="tab" data-id="form-guest-edit-storage" data-toggle="tab">$_("Storage")</a></li>
<li role="presentation"><a href="#form-guest-edit-interface" aria-controls="form-guest-edit-interface" role="tab" data-id="form-guest-edit-interface" data-toggle="tab">$_("Interface")</a></li>
@@ -39,7 +39,7 @@
<li role="presentation"><a href="#form-guest-edit-snapshot" aria-controls="form-guest-edit-snapshot" role="tab" data-id="form-guest-edit-snapshot" data-toggle="tab">$_("Snapshot")</a></li>
<li role="presentation"><a href="#form-edit-processor" aria-controls="form-edit-processor" role="tab" data-id="form-edit-processor" data-toggle="tab">$_("Processor")</a></li>
</ul>
- <div class="tab-content" id="guest-edit-tabs">
+ <div class="tab-content" id="guest-edit-tabs" style="height: 720px;">
<form role="tabpanel" class="tab-pane active" id="form-guest-edit-general">
<div class="form-group">
<label for="guest-edit-id-textbox">$_("Name")</label>
@@ -68,6 +68,21 @@
<option value="virtio">$_("virtio")</option>
</select>
</div>
+ <div class="guest-edit-bootorder tab-pane" id="form-guest-edit-bootorder">
+ <div id="bootOrder">
+ <label for="guest-edit-boot-order-textbox">Boot Order</label>
+ <ul id="myList" class="list-group boot-order">
+ <li class="list-group-item" data-value="CD-ROM">CD-ROM</li>
+ <li class="list-group-item" data-value="HDD">HDD</li>
+ <li class="list-group-item" data-value="Network">Network</li>
+ </ul>
+ <p class="help-block">
+ <i class="fa fa-info-circle"></i> $_("Change the boot order by dragging the items on the list.")</p>
+ </p>
+ <p><button id="guest-edit-add-boot-button" class="add btn btn-primary"><i class="fa fa-plus-circle"></i> $_("Add")</button></p>
+ <div class="body"></div>
+ </div>
+ </div>
</form>
<form role="tabpanel" class="tab-pane" id="form-guest-edit-storage">
<div class="btn-group action-area">
@@ -197,7 +212,6 @@
$_("Current CPU must be equal or lower than the Maximum CPU value. If a topology is set, it must be also be a multiple of the 'threads' value.")
</p>
</div>
-
<div id="guest-max-processor-panel" class="form-group">
<label for="guest-edit-max-processor-textbox">$_("Max CPU")</label>
<p id="settings-readonly-help" class="hidden">$_("Unable to edit maximum CPU or CPU topology when editing a running or paused virtual machine.")</p>
@@ -238,6 +252,21 @@
<button id="guest-edit-button-cancel" class="btn btn-default" data-dismiss="modal">$_("Cancel")</button>
</div>
</div>
+<script id="add-boot-order" type="text/html">
+ <div class="item" id="bootDevOrder">
+ <span class="cell column-device">
+ <select id="bootDev" data-none-selected-text="$_("Nothing selected")">
+ <option id="cdrom-check">cdrom</option>
+ <option id="hdd-check">hd</option>
+ <option id="network-check">network</option>
+ </select>
+ </span>
+ <span class="cell column-actions action-area">
+ <button class="btn btn-primary save">$_("Save")</button>
+ <button class="btn btn-primary cancel">$_("Remove")</button>
+ </span>
+ <div>
+</script>
<script id="cdrom-row-tmpl" type="text/html">
<div class="item view" id="cdrom-{dev}">
<span class="cell dev column-device">
--
2.9.3
7 years, 10 months
[Wok] Do not link user role with UI tabs
by Aline Manera
Hi all,
Today, Wok provides basic authorization level for all the application.
When using PAM authentication, an user with root right (sudo ALL) will
be considered a sysadmin and will have full control no Wok and its plugins.
When using LDAP authentication, the users listed in "admin_users"
parameter in wok.conf will be considered a sysadmin and then having full
control on Wok and its plugins.
Thinking about providing more granularity on authorization (ie, grant
access to a normal user to create a VM on Kimchi, for example) the user
role (sysadmin or normal user) was linked to the UI tabs (:-() instead
of the API itself (you can see it on src/wok/auth.py)
It can cause multiple issues, for example:
- different plugins with the same tab name (the case of Ginger and
Kimchi) will get the authorization settings merged
- what about a tab making using of different APIs? The case of
"Administration" tab on Ginger
So, IMO the better solution would be to have that granularity (when it
will be implemented) by API.
So when a sysadmin may want to grant permission to a normal user to
manage virtual machines, there would be an API like:
POST /config/permission {api: <api>, username: <username>, role: admin|user}
That will store the information in a DB (objectstore) and make use of it
when responding to a request.
with objectstore as session:
user_role = session.get('permission', <api>, <username>)
The reason of this RFC is to change the USER_ROLES we have today in
auth.py to only store if the user is an admin or not and make use of it
in the whole application not linked to any tab. That means, all the
self.role_key parameter will be removed and the UI will be changed as
well to reflect that.
What do you think about it?
I plan to send a patch to remove the link between user role and tabs as
soon as we get agreement on it and the patches about grant permissions
will require more discussions.
Regards,
Aline Manera
7 years, 10 months
[RFC] Plugin management - disable/enable WoK plug-ins
by Daniel Henrique Barboza
Hi there,
I am working in a way to enable/disable WoK plug-ins using an API. This is
what we already have regarding plugin management:
- each plug-in has a configuration file, for example ginger.conf:
[wok]
# Enable Ginger plugin on Wok server (values: True|False)
enable = True
(...)
- an API that returns all enable plug-ins:
$ curl -k -u danielhb -H "Content-Type: application/json" -H "Accept:
application/json" -X GET 'https://localhost:8001/plugins'
Enter host password for user 'danielhb':
[
"gingerbase",
"kimchi",
"ginger"
]
The API retrieves the contents of each plug-in config file and return the
plug-in name if enable = True.
This is my proposal to implement this new feature without messing around
too much:
- the /plugins API will now returns all plug-ins, regardless of the
plug-in being
loaded or not. In the previous example the returned value would be:
[{ "name: "gingerbase", "enabled": True} ,
{ "name: "ginger", "enabled": True},
{ "name: "kimchi", "enabled": True} ]
Changes in the existing API calls will be made considering this new format.
- A new PUT method to change the 'enabled' attribute of the plug-ins.
Changing
the attribute will write the new value in the plug-in config file,
allowing WoK
to enable/disable the plug-in in the next reboot.
- To make the changes effective we need to reboot WoK after
enabling/disabling
a plug-in. This can be done by either rebooting WoK in the same PUT
operation I
mentioned above or by a new 'reboot' API in WoK. I am more fond of a new API
but I can live with this 'super PUT' API too.
Note that at this moment we will not supporting load/unload of plug-ins
without
rebooting WoK. This has proven to be challenging considering our current
plug-in loading schema in cherrypy.
Thoughts?
Daniel
7 years, 10 months
[PATCH][Wok] Bug fix #147: Block authentication request after too many failures
by Ramon Medeiros
To prevent brute force attack, creates a mechanism to allow 3 tries
first. After that, a timeout will start and will be added 30 seconds for
each failed try in a row.
Signed-off-by: Ramon Medeiros <ramonn(a)linux.vnet.ibm.com>
---
src/wok/i18n.py | 1 +
src/wok/root.py | 57 +++++++++++++++++++++++++++++++++++++++++++++---
ui/js/src/wok.login.js | 10 ++++++++-
ui/pages/login.html.tmpl | 3 ++-
4 files changed, 66 insertions(+), 5 deletions(-)
diff --git a/src/wok/i18n.py b/src/wok/i18n.py
index e454e31..21cc4ea 100644
--- a/src/wok/i18n.py
+++ b/src/wok/i18n.py
@@ -41,6 +41,7 @@ messages = {
"WOKAUTH0001E": _("Authentication failed for user '%(username)s'. [Error code: %(code)s]"),
"WOKAUTH0002E": _("You are not authorized to access Wok. Please, login first."),
"WOKAUTH0003E": _("Specify %(item)s to login into Wok."),
+ "WOKAUTH0004E": _("You have failed to login in too much attempts. Please, wait for %(seconds)s seconds to try again."),
"WOKAUTH0005E": _("Invalid LDAP configuration: %(item)s : %(value)s"),
"WOKLOG0001E": _("Invalid filter parameter. Filter parameters allowed: %(filters)s"),
diff --git a/src/wok/root.py b/src/wok/root.py
index 080b7f0..4cabfdf 100644
--- a/src/wok/root.py
+++ b/src/wok/root.py
@@ -1,7 +1,7 @@
#
# Project Wok
#
-# Copyright IBM Corp, 2015-2016
+# Copyright IBM Corp, 2015-2017
#
# Code derived from Project Kimchi
#
@@ -21,7 +21,9 @@
import cherrypy
import json
+import re
import os
+import time
from distutils.version import LooseVersion
from wok import auth
@@ -31,7 +33,7 @@ from wok.config import paths as wok_paths
from wok.control import sub_nodes
from wok.control.base import Resource
from wok.control.utils import parse_request
-from wok.exception import MissingParameter
+from wok.exception import MissingParameter, UnauthorizedError
from wok.reqlogger import log_request
@@ -48,7 +50,8 @@ class Root(Resource):
super(Root, self).__init__(model)
self._handled_error = ['error_page.400', 'error_page.404',
'error_page.405', 'error_page.406',
- 'error_page.415', 'error_page.500']
+ 'error_page.415', 'error_page.500',
+ 'error_page.403']
if not dev_env:
self._cp_config = dict([(key, self.error_production_handler)
@@ -146,6 +149,7 @@ class WokRoot(Root):
self.domain = 'wok'
self.messages = messages
self.extends = None
+ self.failed_logins = {}
# set user log messages and make sure all parameters are present
self.log_map = ROOT_REQUESTS
@@ -153,6 +157,13 @@ class WokRoot(Root):
@cherrypy.expose
def login(self, *args):
+ def _raise_timeout(user_id):
+ length = self.failed_logins[user_ip_sid]["count"]
+ timeout = (length - 3) * 30
+ details = e = UnauthorizedError("WOKAUTH0004E",
+ {"seconds": timeout})
+ log_request(code, params, details, method, 403)
+ raise cherrypy.HTTPError(403, e.message)
details = None
method = 'POST'
code = self.getRequestMessage(method, 'login')
@@ -166,10 +177,50 @@ class WokRoot(Root):
log_request(code, params, details, method, 400)
raise cherrypy.HTTPError(400, e.message)
+ # get authentication info
+ remote_ip = cherrypy.request.remote.ip
+ session_id = str(cherrypy.session.originalid)
+ user_ip_sid = re.escape(username + remote_ip + session_id)
+
+ # check for repetly
+ count = self.failed_logins.get(user_ip_sid, {"count": 0}).get("count")
+ if count >= 3:
+
+ # verify if timeout is still valid
+ last_try = self.failed_logins[user_ip_sid]["time"]
+ if time.time() < (last_try + ((count - 3) * 30)):
+ _raise_timeout(user_ip_sid)
+
try:
status = 200
user_info = auth.login(username, password)
+
+ # user logged sucessfuly: reset counters
+ if self.failed_logins.get(user_ip_sid) != None:
+ self.failed_logins.remove(user_ip_sid)
except cherrypy.HTTPError, e:
+
+ # store time and prevent too much tries
+ if self.failed_logins.get(user_ip_sid) == None:
+ self.failed_logins[user_ip_sid] = {"time": time.time(),
+ "ip": remote_ip,
+ "session_id": session_id,
+ "username": username,
+ "count": 1}
+ else:
+
+ # tries take more than 30 seconds between each one: do not
+ # increase count
+ if (time.time() -
+ self.failed_logins[user_ip_sid]["time"]) < 30:
+
+ self.failed_logins[user_ip_sid]["time"] = time.time()
+ self.failed_logins[user_ip_sid]["count"] += 1
+
+ # more than 3 fails: raise error
+ if self.failed_logins[user_ip_sid]["count"] > 3:
+ _raise_timeout(user_ip_sid)
+
status = e.status
raise
finally:
diff --git a/ui/js/src/wok.login.js b/ui/js/src/wok.login.js
index fa2a98a..12c6880 100644
--- a/ui/js/src/wok.login.js
+++ b/ui/js/src/wok.login.js
@@ -1,7 +1,7 @@
/*
* Project Wok
*
- * Copyright IBM Corp, 2015-2016
+ * Copyright IBM Corp, 2015-2017
*
* Code derived from Project Kimchi
*
@@ -84,9 +84,17 @@ wok.login_main = function() {
if (jqXHR.responseText == "") {
$("#messUserPass").hide();
$("#missServer").show();
+ $("#timeoutError").hide();
+ } else if ((jqXHR.responseJSON != undefined) &&
+ ! (jqXHR.responseJSON["reason"] == undefined)) {
+ $("#messUserPass").hide();
+ $("#missServer").hide();
+ $("#timeoutError").html(jqXHR.responseJSON["reason"]);
+ $("#timeoutError").show();
} else {
$("#missServer").hide();
$("#messUserPass").show();
+ $("#timeoutError").hide();
}
$("#messSession").hide();
$("#logging").hide();
diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl
index f5a4b2d..d25910c 100644
--- a/ui/pages/login.html.tmpl
+++ b/ui/pages/login.html.tmpl
@@ -1,7 +1,7 @@
#*
* Project Wok
*
- * Copyright IBM Corp, 2014-2016
+ * Copyright IBM Corp, 2014-2017
*
* Code derived from Project Kimchi
*
@@ -107,6 +107,7 @@
<div id="messUserPass" class="alert alert-danger" style="display: none;">$_("The username or password you entered is incorrect. Please try again.")</div>
<div id="messSession" class="alert alert-danger" style="display: none;">$_("Session timeout, please re-login.")</div>
<div id="missServer" class="alert alert-danger" style="display: none;">$_("Server unreachable.")</div>
+ <div id="timeoutError" class="alert alert-danger" style="display: none;">$_("Timeout error")</div>
</div>
<form id="form-login" class="form-horizontal" method="post">
<div class="form-group">
--
2.7.4
7 years, 10 months
nginx server and it's default port number (80)
by Chandra Shekhar Reddy Potula
Hi All,
By default nginx server configured with port number 80 and I see the
following from netstat
In the file /etc/nginx/nginx.conf
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
[root@zs95kc conf.d]# netstat -anp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address
State PID/Program name
tcp 0 0 0.0.0.0:8000 0.0.0.0:*
LISTEN 57091/nginx: master
tcp 0 0 0.0.0.0:8001 0.0.0.0:*
LISTEN 57091/nginx: master
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
57091/nginx: master
tcp 0 0 192.168.122.1:53 0.0.0.0:*
LISTEN 15237/dnsmasq
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
12897/sshd
tcp 0 248 10.20.92.162:22 10.20.94.36:49404
ESTABLISHED 56319/sshd: root@pt
tcp6 0 0 :::80 :::* LISTEN
57091/nginx: master
tcp6 0 0 :::22 :::* LISTEN
12897/sshd
udp 0 0 192.168.122.1:53 0.0.0.0:* 15237/dnsmasq
udp 0 0 0.0.0.0:67 0.0.0.0:*
15237/dnsmasq
raw6 0 0 :::58 :::* 7
12181/NetworkManage
So the question I have is do we really require nginx listing on port
number 80 by wok ?
Thanks in advance,
Regards
Chandra
7 years, 10 months
[RFC] [Wok] #147 Block authentication request after too many failures
by Ramon Medeiros
Propose: make adjustments at login page to make difficult brute force
attack.
Today, an intruder can make login tries without any action from Wok.
Possible measures:
Record source port and ip. After 3 tries, block user for 30 seconds and
increase the time by each more try. Using source port and ip will avoid
errors for connections from NAT networks.
Example:
1) ip 192.168.1.1 tries to login as root 3 times and fail
2) A timeout of 30 seconds will be set
3) After that, for 5 minutes, each try will add 30 seconds + x times the
trial (60 seconds, 90 seconds. ..)
4) After 5 minutes of the last try, the counter will be reset.
--
Ramon Nunes Medeiros
Kimchi Developer
Linux Technology Center Brazil
IBM Systems & Technology Group
Phone : +55 19 2132 7878
ramonn(a)br.ibm.com
7 years, 10 months