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 var tr = self.parentNode.parentNode
228 var img = tr.querySelector('img')
229 if (img.getAttribute('src') != window.img["red
"])
231 bt_action.disabled = false
232 return info_message(output_list,"Container is still running!
")
235 if (!confirm("This will completely remove a stopped LXC container from disk. Are you sure?
"))
240 new XHR().get('<%=luci.dispatcher.build_url("admin
", "services
")%>/lxc_action/' + '%h/%h'.format(action, lxc_name), null,
244 bt_action.disabled = false
247 return info_message(output_list,"Action failed!
")
249 var row = self.parentNode.parentNode
250 row.parentNode.removeChild(row)
256 function lxc_configure_handler(self)
258 var td = self.parentNode
259 var textarea = td.querySelector('[data-id]')
260 var lxc_name = textarea.getAttribute('data-id')
261 var lxc_configuration = textarea.value
263 new XHR().post('<%=luci.dispatcher.build_url("admin
", "services
")%>/lxc_configuration_set/' + lxc_name, "lxc_configuration=
" + encodeURIComponent(lxc_configuration) ,
266 if (!x || x.responseText != "0")
267 return info_message(output_list,"Action failed!
")
269 info_message(output_list,"LXC configuration updated
")
270 var row = td.parentNode
271 row.parentNode.removeChild(row)
275 function lxc_rename_template(lxc_name)
278 <input data-id="'+ lxc_name + '
" type="text
" placeholder="Enter new name
" /> \
279 <input data-id="bt_confirm
" onclick="lxc_rename_handler(this)
" type="button
" class="cbi-button
" value="Confirm
" />'
284 function lxc_configure_template(lxc_name, lxc_configuration)
287 <textarea data-id="'+ lxc_name + '
" rows="20" style="width:
100%
">'+ lxc_configuration +'</textarea> \
288 <input data-id="bt_confirm
" onclick="lxc_configure_handler(this)
" type="button
" class="cbi-button
" value="Confirm
" />'
293 function action_more_handler(self)
295 var lxc_name = self.parentNode.parentNode.querySelector('[data-id]').getAttribute('data-id')
296 var loader = self.parentNode.parentNode.querySelector('[data-loader]')
297 var option = self.options[self.selectedIndex].text
304 var tr = document.createElement('tr')
305 var row = self.parentNode.parentNode
306 var next_row = row.nextSibling
307 if (next_row && next_row.getAttribute('data-action') !== null)
308 row.parentNode.removeChild(next_row)
310 new XHR().get('<%=luci.dispatcher.build_url("admin
", "services
")%>/lxc_configuration_get/' + lxc_name, null,
313 tr.innerHTML="<td colspan='
" + row.cells.length + "'
>" + lxc_configure_template(lxc_name, x.responseText) + "</td>"
314 tr.setAttribute('data-action','')
315 row.parentNode.insertBefore(tr, row.nextSibling)
321 var tr = self.parentNode.parentNode
322 var img = tr.querySelector('img')
323 if(img.getAttribute('src') != window.img["green
"])
324 return info_message(output_list,"Container is not running!
")
327 new XHR().get('<%=luci.dispatcher.build_url("admin
", "services
")%>/lxc_action/' + '%h/%h'.format(option, lxc_name), null,
332 return info_message(output_list,"Action failed!
")
334 set_status(img, "purple
")
340 var tr = self.parentNode.parentNode
341 var img = tr.querySelector('img')
343 if(img.getAttribute('src') != window.img["purple
"])
344 return info_message(output_list,"Container is not frozen!
")
347 new XHR().get('<%=luci.dispatcher.build_url("admin
", "services
")%>/lxc_action/' + '%h/%h'.format(option, lxc_name), null,
352 return info_message(output_list,"Action failed!
")
354 set_status(img, "green
")
360 var tr = self.parentNode.parentNode
361 var img = tr.querySelector('img')
362 if(img.getAttribute('src') != window.img["green
"])
363 return info_message(output_list,"Container is not running!
")
365 if (!confirm("Are you sure?
"))
369 new XHR().get('<%=luci.dispatcher.build_url("admin
", "services
")%>/lxc_action/' + '%h/%h'.format(option, lxc_name), null,
374 return info_message(output_list,"Action failed!
")
376 info_message(output_list,"LXC rebooted
")
383 function set_empty(t_lxc_list)
385 if (document.getElementById('empty') !== null)
388 var row_count = t_lxc_list.rows.length;
389 while(--row_count) t_lxc_list.deleteRow(row_count);
391 var row = t_lxc_list.insertRow(-1);
393 var cell = row.insertCell(0);
395 cell.innerHTML = '<em><br />There are no containers available yet.</em>';
398 function set_empty_template()
400 var row_count = t_lxc_create.rows.length;
401 while(--row_count) t_lxc_create.deleteRow(row_count);
403 var row = t_lxc_create.insertRow(-1);
405 var cell = row.insertCell(0);
407 cell.innerHTML = '<em><br />There are no templates for your architecture (<%=target%>) available, please select another containers URL.</em>';
410 function lxc_list_update()
412 XHR.poll(4, '<%=luci.dispatcher.build_url("admin
", "services
")%>/lxc_action/list', null,
418 return set_empty(t_lxc_list)
420 var lxc_count = Object.keys(data).length
422 return set_empty(t_lxc_list)
424 if (document.getElementById('empty') !== null)
425 t_lxc_list.deleteRow(1);
427 var lxcs = t_lxc_list.querySelectorAll('td[data-id]')
428 var lxc_name_table = {}
429 for (var i = 0, len = lxcs.length; i < len; i++)
431 var lxc_name = lxcs[i].getAttribute('data-id')
432 if (!(lxc_name in data))
434 var row = t_lxc_list.querySelector("[data-id='
" + lxc_name + "']
").parentNode
435 row.parentNode.removeChild(row)
439 lxc_name_table[lxc_name] = lxcs[i].parentNode.querySelector('img')
445 var state = window.states[data[key]]
447 if (!(lxc_name in lxc_name_table))
448 lxc_create_template(lxc_name, state)
450 else if (state != get_status(lxc_name_table[lxc_name]))
451 set_status(lxc_name_table[lxc_name], state)
457 function loading(elem, state)
459 state = (typeof state === 'undefined') ? 1 : state
462 elem.innerHTML = loader_html
464 setTimeout(function() { elem.innerHTML = ''}, 1000)
467 function set_status(elem, state)
469 state = (typeof state === 'undefined') ? 1 : state
471 setTimeout(function() { elem.setAttribute('src', window.img[state])}, 300)
474 function get_status(elem)
476 var src = elem.getAttribute('src')
485 function info_message(output, msg, timeout)
487 timeout = timeout || 3000
488 output.innerHTML = msg
489 clearTimeout(timeout_msg)
490 timeout_msg = setTimeout(function(){ output.innerHTML=""}, timeout);
495 new XHR().get('<%=luci.dispatcher.build_url("admin
", "services
")%>/lxc_get_downloadable', null,
500 if (!data) return set_empty_template();
502 var lxc_count = Object.keys(data).length;
503 if (!lxc_count) return set_empty_template();
505 var select = document.getElementById("s_template
");
508 var option = document.createElement('option');
509 option.value = data[key];
510 option.text = data[key].replace(/[_:]/g, ' ');
511 select.add(option, -1);