5 Copyright (C) 2014, Cisco Systems, Inc.
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
11 http://www.apache.org/licenses/LICENSE-2.0
13 Author: Petar Koretic <petar.koretic@sartura.hr>
18 local fs = require "nixio"
19 local target = fs.uname().machine
22 <style type="text/css">
23 table.cbi-section-table th,
24 table.cbi-section-table td,
25 .cbi-section-table-cell,
43 <fieldset class="cbi-section">
44 <legend><%:Available Containers%></legend>
45 <div class="cbi-section-node">
46 <table id="t_lxc_list" class="cbi-section-table">
47 <tr class="cbi-section-table-titles">
48 <th class="cbi-section-table-cell"><%:Name%></th>
49 <th class="cbi-section-table-cell"><%:Status%></th>
50 <th class="cbi-section-table-cell"><%:Actions%></th>
56 <fieldset class="cbi-section">
57 <span id="lxc-list-output"></span>
61 <fieldset class="cbi-section">
62 <legend><%:Create New Container%></legend>
63 <div class="cbi-section-node">
64 <table id="t_lxc_create" class="cbi-section-table">
65 <tr class="cbi-section-table-titles">
66 <th class="cbi-section-table-cell"><%:Name%></th>
67 <th class="cbi-section-table-cell"><%:Template%></th>
68 <th class="cbi-section-table-cell"><%:Actions%></th>
72 <input class="cbi-input-text" type="text" id="tx_name" placeholder="<%:Enter new name%>" value='' />
75 <select id="s_template" class="cbi-input-select cbi-button">
79 <input type="button" id="bt_create" value="<%:Create%>" onclick="lxc_create(tr_holder)" class="cbi-button cbi-button-add" />
80 <span id="lxc-add-loader" style="display:inline-block; width:16px; height:16px; margin:0 5px"></span>
87 <fieldset class="cbi-section">
88 <span id="lxc-add-output"></span>
93 <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
94 <script type="text/javascript">
96 window.img = { "red" : "<%=resource%>/cbi/red.gif", "green" : "<%=resource%>/cbi/green.gif", "purple" : "<%=resource%>/cbi/purple.gif" }
97 window.states = { "STOPPED" : "red", "RUNNING" : "green", "FROZEN" : "purple" }
99 var t_lxc_list = document.getElementById('t_lxc_list');
100 var loader_html = '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" width="16" height="16" style="vertical-align:middle" />';
102 var output_list = document.getElementById("lxc-list-output")
103 var output_add = document.getElementById("lxc-add-output")
104 var loader_add = document.getElementById("lxc-add-loader")
106 info_message(output_add, "Template download in progress, please be patient!", 10000)
108 function lxc_create(tr)
110 var lxc_name = tr.querySelector("#tx_name").value.replace(/\s/g,'')
111 var lxc_template = tr.querySelector("#s_template").value
112 var bt_create = tr.querySelector("#bt_create")
114 if (t_lxc_list.querySelector("[data-id='" + lxc_name + "']") != null)
115 return info_message(output_add, "Container with that name already exists!", 4000)
117 bt_create.disabled = true
118 output_add.innerHTML = ''
120 if (!lxc_name || !lxc_name.length)
122 bt_create.disabled = false
123 return info_message(output_add, "The 'Name' field must not be empty!", 4000)
128 bt_create.disabled = false
129 return info_message(output_add, "The 'Template' field must not be empty!", 4000)
134 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_create/' + '%h/%h'.format(lxc_name, lxc_template) , null,
137 bt_create.disabled = false
139 loading(loader_add, 0)
142 info_message(output_add, "Container creation failed!")
146 function lxc_create_template(lxc_name, lxc_state)
148 var info_row = t_lxc_list.querySelector("#empty")
150 t_lxc_list.deleteRow(1)
153 actions += '<input type="button" onclick="action_handler(this)" data-action="start" value="<%:Start%>" class="cbi-button cbi-button-apply" />'
154 actions += ' <input type="button" onclick="action_handler(this)" data-action="stop" value="<%:Stop%>" class="cbi-button cbi-button-reset" />'
155 actions += ' <input type="button" onclick="action_handler(this)" data-action="destroy" value="<%:Delete%>" class="cbi-button cbi-button-remove" />'
156 actions += ' <select class="cbi-input-select cbi-button" onchange="action_more_handler(this)">\
157 <option selected="selected" disabled="disabled">more</option>\
158 <option>configure</option>\
159 <option>freeze</option>\
160 <option>unfreeze</option>\
161 <option>reboot</option>\
163 actions += '<span data-loader="" style="display:inline-block; width:16px; height:16px; margin:0 5px"></span>'
165 var row = t_lxc_list.insertRow(-1)
166 var cell = row.insertCell(-1)
167 cell.innerHTML = '%q%h%q'.format("<strong>", lxc_name, "</strong>")
169 cell.setAttribute("data-id", lxc_name)
171 cell = row.insertCell(-1)
173 cell.innerHTML = "<img src='"+window.img[lxc_state]+"'/>"
175 cell = row.insertCell(-1)
177 cell.innerHTML = actions
180 function action_handler(self)
182 var action = self.getAttribute("data-action");
184 var lxc_name = self.parentNode.parentNode.children[0].getAttribute('data-id')
185 var status_img = self.parentNode.parentNode.querySelector('img')
186 var loader = self.parentNode.querySelector('[data-loader]')
188 bt_action.disabled = true
190 if (action == "stop")
194 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_action/' + '%h/%h'.format(action, lxc_name), null,
198 bt_action.disabled = false
201 return info_message(output_list,"Action failed!")
203 set_status(status_img, "red")
208 else if (action == "start")
212 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_action/' + '%h/%h'.format(action, lxc_name), null,
216 bt_action.disabled = false
219 return info_message(output_list,"Action failed!")
221 set_status(status_img, "green")
225 else if (action == "destroy")
227 if (!confirm("This will completely remove a stopped LXC container from disk. Are you sure?"))
232 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_action/' + '%h/%h'.format(action, lxc_name), null,
236 bt_action.disabled = false
239 return info_message(output_list,"Action failed!")
241 var row = self.parentNode.parentNode
242 row.parentNode.removeChild(row)
248 function lxc_configure_handler(self)
250 var td = self.parentNode
251 var textarea = td.querySelector('[data-id]')
252 var lxc_name = textarea.getAttribute('data-id')
253 var lxc_configuration = textarea.value
255 new XHR().post('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_configuration_set/' + lxc_name, "lxc_configuration=" + encodeURIComponent(lxc_configuration) ,
258 if (!x || x.responseText != "0")
259 return info_message(output_list,"Action failed!")
261 info_message(output_list,"LXC configuration updated")
262 var row = td.parentNode
263 row.parentNode.removeChild(row)
267 function lxc_rename_template(lxc_name)
270 <input data-id="'+ lxc_name + '" type="text" placeholder="Enter new name" /> \
271 <input data-id="bt_confirm" onclick="lxc_rename_handler(this)" type="button" class="cbi-button" value="Confirm" />'
276 function lxc_configure_template(lxc_name, lxc_configuration)
279 <textarea data-id="'+ lxc_name + '" rows="20" style="width:100%">'+ lxc_configuration +'</textarea> \
280 <input data-id="bt_confirm" onclick="lxc_configure_handler(this)" type="button" class="cbi-button" value="Confirm" />'
285 function action_more_handler(self)
287 var lxc_name = self.parentNode.parentNode.querySelector('[data-id]').getAttribute('data-id')
288 var loader = self.parentNode.parentNode.querySelector('[data-loader]')
289 var option = self.options[self.selectedIndex].text
296 var tr = document.createElement('tr')
297 var row = self.parentNode.parentNode
298 var next_row = row.nextSibling
299 if (next_row && next_row.getAttribute('data-action') !== null)
300 row.parentNode.removeChild(next_row)
302 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_configuration_get/' + lxc_name, null,
305 tr.innerHTML="<td colspan='" + row.cells.length + "'>" + lxc_configure_template(lxc_name, x.responseText) + "</td>"
306 tr.setAttribute('data-action','')
307 row.parentNode.insertBefore(tr, row.nextSibling)
313 var tr = self.parentNode.parentNode
314 var img = tr.querySelector('img')
315 if(img.getAttribute('src') != window.img["green"])
316 return info_message(output_list,"Container is not running!")
319 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_action/' + '%h/%h'.format(option, lxc_name), null,
324 return info_message(output_list,"Action failed!")
326 set_status(img, "purple")
332 var tr = self.parentNode.parentNode
333 var img = tr.querySelector('img')
335 if(img.getAttribute('src') != window.img["purple"])
336 return info_message(output_list,"Container is not frozen!")
339 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_action/' + '%h/%h'.format(option, lxc_name), null,
344 return info_message(output_list,"Action failed!")
346 set_status(img, "green")
352 var tr = self.parentNode.parentNode
353 var img = tr.querySelector('img')
354 if(img.getAttribute('src') != window.img["green"])
355 return info_message(output_list,"Container is not running!")
357 if (!confirm("Are you sure?"))
361 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_action/' + '%h/%h'.format(option, lxc_name), null,
366 return info_message(output_list,"Action failed!")
368 info_message(output_list,"LXC rebooted")
375 function set_empty(t_lxc_list)
377 if (document.getElementById('empty') !== null)
380 var row_count = t_lxc_list.rows.length;
381 while(--row_count) t_lxc_list.deleteRow(row_count);
383 var row = t_lxc_list.insertRow(-1);
385 var cell = row.insertCell(0);
387 cell.innerHTML = '<em><br />There are no containers available yet.</em>';
390 function set_empty_template()
392 if (document.getElementById('tr_holder') !== null)
395 var row_count = t_lxc_create.rows.length;
396 while(--row_count) t_lxc_create.deleteRow(row_count);
398 var row = t_lxc_create.insertRow(-1);
400 var cell = row.insertCell(0);
402 cell.innerHTML = '<em><br />There are no templates for your architecture (<%=target%>) available, please select another Containers URL.</em>';
405 function lxc_list_update()
407 XHR.poll(4, '<%=luci.dispatcher.build_url("admin", "services")%>/lxc_action/list', null,
413 return set_empty(t_lxc_list)
415 var lxc_count = Object.keys(data).length
417 return set_empty(t_lxc_list)
419 if (document.getElementById('empty') !== null)
420 t_lxc_list.deleteRow(1);
422 var lxcs = t_lxc_list.querySelectorAll('td[data-id]')
423 var lxc_name_table = {}
424 for (var i = 0, len = lxcs.length; i < len; i++)
426 var lxc_name = lxcs[i].getAttribute('data-id')
427 if (!(lxc_name in data))
429 var row = t_lxc_list.querySelector("[data-id='" + lxc_name + "']").parentNode
430 row.parentNode.removeChild(row)
434 lxc_name_table[lxc_name] = lxcs[i].parentNode.querySelector('img')
440 var state = window.states[data[key]]
442 if (!(lxc_name in lxc_name_table))
443 lxc_create_template(lxc_name, state)
445 else if (state != get_status(lxc_name_table[lxc_name]))
446 set_status(lxc_name_table[lxc_name], state)
452 function loading(elem, state)
454 state = (typeof state === 'undefined') ? 1 : state
457 elem.innerHTML = loader_html
459 setTimeout(function() { elem.innerHTML = ''}, 1000)
462 function set_status(elem, state)
464 state = (typeof state === 'undefined') ? 1 : state
466 setTimeout(function() { elem.setAttribute('src', window.img[state])}, 300)
469 function get_status(elem)
471 var src = elem.getAttribute('src')
480 function info_message(output, msg, timeout)
482 timeout = timeout || 3000
483 output.innerHTML = msg
484 clearTimeout(timeout_msg)
485 timeout_msg = setTimeout(function(){ output.innerHTML=""}, timeout);
490 new XHR().get('<%=luci.dispatcher.build_url("admin", "services")%>/lxc_get_downloadable', null,
495 if (!data) return set_empty_template();
497 var lxc_count = Object.keys(data).length;
498 if (!lxc_count) return set_empty_template();
500 var select = document.getElementById("s_template");
503 var option = document.createElement('option');
504 option.value = data[key];
505 option.text = data[key].replace(/[_:]/g, ' ');
506 select.add(option, -1);