gandetails.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. <div class="standardContainer">
  2. <button onclick="exitToGanList();" class="ui large circular black icon button"><i class="angle left icon"></i></button>
  3. <div style="max-width: 300px; margin-top: 1em;">
  4. <button onclick='$("#gannetDetailEdit").slideToggle("fast");' class="ui mini basic right floated circular icon button" style="display: inline-block; margin-top: 2.5em;"><i class="ui edit icon"></i></button>
  5. <h1 class="ui header">
  6. <span class="ganetID"></span>
  7. <div class="sub header ganetName"></div>
  8. </h1>
  9. <div class="ui divider"></div>
  10. <p><span class="ganetDesc"></span></p>
  11. </div>
  12. <div id="gannetDetailEdit" class="ui form" style="margin-top: 1em; display:none;">
  13. <div class="ui divider"></div>
  14. <p>You can change the network name and description below. <br>The name and description is only for easy management purpose and will not effect the network operation.</p>
  15. <div class="field">
  16. <label>Network Name</label>
  17. <input type="text" id="gaNetNameInput" placeholder="">
  18. </div>
  19. <div class="field">
  20. <label>Network Description</label>
  21. <textarea id="gaNetDescInput" style="resize: none;"></textarea>
  22. <button onclick="saveNameAndDesc(this);" class="ui basic right floated button" style="margin-top: 0.6em;"><i class="ui save icon"></i> Save</button>
  23. <button onclick='$("#gannetDetailEdit").slideUp("fast");' class="ui basic right floated button" style="margin-top: 0.6em;"><i class="ui red remove icon"></i> Cancel</button>
  24. </div>
  25. </div>
  26. <div class="ui divider"></div>
  27. <h2>Settings</h2>
  28. <div class="" style="overflow-x: auto;">
  29. <table class="ui basic celled unstackable table" style="min-width: 560px;">
  30. <thead>
  31. <tr>
  32. <th colspan="4">IPv4 Auto-Assign</th>
  33. </tr>
  34. </thead>
  35. <tbody id="ganetRangeTable">
  36. </tbody>
  37. </table>
  38. </div>
  39. <div class="ui basic segment form">
  40. <div class="unstackable fields">
  41. <div class="ten wide field">
  42. <label>Multicast Recipient Limit</label>
  43. <input type="number" id="" placeholder="32" value="32">
  44. </div>
  45. <div class="six wide field">
  46. <div class="ui toggle checkbox" style="margin-top: 2.3em; padding-left: 0.6em;">
  47. <label>Enable Multicast</label>
  48. <input type="checkbox" tabindex="0" class="hidden">
  49. </div>
  50. </div>
  51. </div>
  52. </div>
  53. <div class="ui divider"></div>
  54. <h2>Members</h2>
  55. <div class="ui checkbox" style="margin-bottom: 1em;">
  56. <input id="showUnauthorizedMembers" type="checkbox" onchange="changeUnauthorizedVisibility(this.checked);">
  57. <label>Show Unauthorized Members</label>
  58. </div>
  59. <div class="" style="overflow-x: auto;">
  60. <table class="ui celled unstackable table">
  61. <thead>
  62. <tr>
  63. <th>Auth</th>
  64. <th>Address</th>
  65. <th>Name/Description</th>
  66. <th>Managed IP</th>
  67. <th>Last Seen</th>
  68. <th>Version</th>
  69. <th>Remove</th>
  70. </tr>
  71. </thead>
  72. <tbody id="networkMemeberTable">
  73. <tr>
  74. </tr>
  75. </tbody>
  76. </table>
  77. </div>
  78. <br><br>
  79. </div>
  80. <script>
  81. $(".checkbox").checkbox();
  82. var currentGANetID = "";
  83. var currentGANNetMemeberListener = undefined;
  84. var currentGaNetDetails = {};
  85. var currentGANMemberList = [];
  86. var netRanges = {
  87. "10.147.17.*": "10.147.17.0/24",
  88. "10.147.18.*": "10.147.18.0/24",
  89. "10.147.19.*": "10.147.19.0/24",
  90. "10.147.20.*": "10.147.20.0/24",
  91. "10.144.*.*": "10.144.0.0/16",
  92. "10.241.*.*": "10.241.0.0/16",
  93. "10.242.*.*": "10.242.0.0/16",
  94. "10.243.*.*": "10.243.0.0/16",
  95. "10.244.*.*": "10.244.0.0/16",
  96. "172.22.*.*": "172.22.0.0/15",
  97. "172.23.*.*": "172.23.0.0/16",
  98. "172.24.*.*": "172.24.0.0/14",
  99. "172.25.*.*": "172.25.0.0/16",
  100. "172.26.*.*": "172.26.0.0/15",
  101. "172.27.*.*": "172.27.0.0/16",
  102. "172.28.*.*": "172.28.0.0/15",
  103. "172.29.*.*": "172.29.0.0/16",
  104. "172.30.*.*": "172.30.0.0/15",
  105. "192.168.191.*": "192.168.191.0/24",
  106. "192.168.192.*": "192.168.192.0/24",
  107. "192.168.193.*": "192.168.193.0/24",
  108. "192.168.194.*": "192.168.194.0/24",
  109. "192.168.195.*": "192.168.195.0/24",
  110. "192.168.196.*": "192.168.196.0/24"
  111. }
  112. function generateIPRangeTable(netRanges) {
  113. $("#ganetRangeTable").empty();
  114. const tableBody = document.getElementById('ganetRangeTable');
  115. const cidrs = Object.values(netRanges);
  116. // Set the number of rows and columns to display in the table
  117. const numRows = 6;
  118. const numCols = 4;
  119. let row = document.createElement('tr');
  120. let col = 0;
  121. for (let i = 0; i < cidrs.length; i++) {
  122. if (col >= numCols) {
  123. tableBody.appendChild(row);
  124. row = document.createElement('tr');
  125. col = 0;
  126. }
  127. const td = document.createElement('td');
  128. td.setAttribute('class', `clickable iprange`);
  129. td.setAttribute('CIDR', cidrs[i]);
  130. td.innerHTML = cidrs[i];
  131. let thisCidr = cidrs[i];
  132. td.onclick = function(){
  133. selectNetworkRange(thisCidr);
  134. };
  135. row.appendChild(td);
  136. col++;
  137. }
  138. // Add any remaining cells to the table
  139. if (col > 0) {
  140. for (let i = col; i < numCols; i++) {
  141. row.appendChild(document.createElement('td'));
  142. }
  143. tableBody.appendChild(row);
  144. }
  145. }
  146. function highlightCurrentGANetCIDR(){
  147. var currentCIDR = currentGaNetDetails.routes[0].target;
  148. $(".iprange").each(function(){
  149. if ($(this).attr("CIDR") == currentCIDR){
  150. $(this).addClass("active");
  151. }
  152. })
  153. }
  154. function selectNetworkRange(cidr){
  155. alert(cidr);
  156. }
  157. function saveNameAndDesc(object=undefined){
  158. var name = $("#gaNetNameInput").val();
  159. var desc = $("#gaNetDescInput").val();
  160. if (object != undefined){
  161. $(object).addClass("loading");
  162. }
  163. $.ajax({
  164. url: "/api/gan/network/name",
  165. method: "POST",
  166. data: {
  167. netid: currentGANetID,
  168. name: name,
  169. desc: desc,
  170. },
  171. success: function(data){
  172. initNetNameAndDesc();
  173. if (object != undefined){
  174. $(object).removeClass("loading");
  175. msgbox("Network Metadata Updated");
  176. }
  177. }
  178. });
  179. }
  180. function initNetNameAndDesc(){
  181. //Get the details of the net
  182. $.get("/api/gan/network/name?netid=" + currentGANetID, function(data){
  183. if (data.error !== undefined){
  184. msgbox(data.error, false, 6000);
  185. }else{
  186. $("#gaNetNameInput").val(data[0]);
  187. $(".ganetName").html(data[0]);
  188. $("#gaNetDescInput").val(data[1]);
  189. $(".ganetDesc").text(data[1]);
  190. }
  191. });
  192. }
  193. function initNetDetails(){
  194. //Get the details of the net
  195. $.get("/api/gan/network/list?netid=" + currentGANetID, function(data){
  196. if (data.error !== undefined){
  197. msgbox(data.error, false, 6000);
  198. }else{
  199. currentGaNetDetails = data;
  200. highlightCurrentGANetCIDR();
  201. }
  202. });
  203. }
  204. //Member table populate
  205. function renderMemeberTable(forceUpdate = false) {
  206. $.ajax({
  207. url: '/api/gan/members/list?netid=e7dd1ce7bfd3b1f9&detail=true',
  208. type: 'GET',
  209. success: function(data) {
  210. const tableBody = $('#networkMemeberTable');
  211. data.sort((a, b) => a.address.localeCompare(b.address));
  212. //Check if the new object equal to the old one
  213. if (objectEqual(currentGANMemberList, data) && !forceUpdate){
  214. //Do not need to update it
  215. return;
  216. }
  217. tableBody.empty();
  218. currentGANMemberList = data;
  219. data.forEach((member) => {
  220. let lastAuthTime = new Date(member.lastAuthorizedTime).toLocaleString();
  221. if (member.lastAuthorizedTime == 0){
  222. lastAuthTime = "Never";
  223. }
  224. let version = `${member.vMajor}.${member.vMinor}.${member.vProto}.${member.vRev}`;
  225. if (member.vMajor == -1){
  226. version = "Unknown";
  227. }
  228. let authorizedCheckbox = `<div class="ui fitted checkbox">
  229. <input type="checkbox" addr="${member.address}" name="isAuthrozied" onchange="handleMemberAuth(this);">
  230. <label></label>
  231. </div>`;
  232. if (member.authorized){
  233. authorizedCheckbox = `<div class="ui fitted checkbox">
  234. <input type="checkbox" addr="${member.address}" name="isAuthrozied" onchange="handleMemberAuth(this);" checked="">
  235. <label></label>
  236. </div>`
  237. }
  238. let rowClass = "authorized";
  239. let unauthorizedStyle = "";
  240. if (!$("#showUnauthorizedMembers")[0].checked && !member.authorized){
  241. unauthorizedStyle = "display:none;";
  242. }
  243. if (!member.authorized){
  244. rowClass = "unauthorized"
  245. }
  246. const row = $(`<tr class="GANetMemberEntity ${rowClass}" style="${unauthorizedStyle}">`);
  247. row.append($(`<td class="GANetMember ${rowClass}" style="text-align: center;">`).html(authorizedCheckbox));
  248. row.append($('<td>').text(member.address));
  249. row.append($('<td>').text(""));
  250. row.append($('<td>').text(member.ipAssignments || ''));
  251. row.append($('<td>').text(lastAuthTime));
  252. row.append($('<td>').text(version));
  253. row.append($(`<td style="text-align: center;" onclick="handleMemberDelete('${member.address}');">`).html(`<button class="ui basic mini icon button"><i class="red remove icon"></i></button>`));
  254. tableBody.append(row);
  255. });
  256. },
  257. error: function(xhr, status, error) {
  258. console.log('Error:', error);
  259. }
  260. });
  261. }
  262. function objectEqual(obj1, obj2) {
  263. // compare types
  264. if (typeof obj1 !== typeof obj2) {
  265. return false;
  266. }
  267. // compare values
  268. if (typeof obj1 !== 'object' || obj1 === null) {
  269. return obj1 === obj2;
  270. }
  271. const keys1 = Object.keys(obj1);
  272. const keys2 = Object.keys(obj2);
  273. // compare keys
  274. if (keys1.length !== keys2.length) {
  275. return false;
  276. }
  277. for (const key of keys1) {
  278. if (!keys2.includes(key)) {
  279. return false;
  280. }
  281. // recursively compare values
  282. if (!objectEqual(obj1[key], obj2[key])) {
  283. return false;
  284. }
  285. }
  286. return true;
  287. }
  288. function changeUnauthorizedVisibility(visable){
  289. if(visable){
  290. $(".GANetMemberEntity.unauthorized").show();
  291. }else{
  292. $(".GANetMemberEntity.unauthorized").hide();
  293. }
  294. }
  295. function handleMemberAuth(object){
  296. let targetMemberAddr = $(object).attr("addr");
  297. let isAuthed = object.checked;
  298. $.ajax({
  299. url: "/api/gan/members/authorize",
  300. method: "POST",
  301. data: {
  302. netid:currentGANetID,
  303. memid: targetMemberAddr,
  304. auth: isAuthed
  305. },
  306. success: function(data){
  307. if (data.error != undefined){
  308. msgbox(data.error, false, 6000);
  309. }else{
  310. if (isAuthed){
  311. msgbox("Member Authorized");
  312. }else{
  313. msgbox("Member Deauthorized");
  314. }
  315. }
  316. renderMemeberTable(true);
  317. }
  318. })
  319. }
  320. function handleMemberDelete(addr){
  321. if (confirm("Confirm delete member " + addr + " ?")){
  322. $.ajax({
  323. url: "/api/gan/members/delete",
  324. method: "POST",
  325. data: {
  326. netid:currentGANetID,
  327. memid: addr,
  328. },
  329. success: function(data){
  330. if (data.error != undefined){
  331. msgbox(data.error, false, 6000);
  332. }else{
  333. msgbox("Member Deleted");
  334. }
  335. renderMemeberTable(true);
  336. }
  337. });
  338. }
  339. }
  340. //Entry points
  341. function initGanetDetails(ganetId){
  342. currentGANetID = ganetId;
  343. $(".ganetID").text(ganetId);
  344. initNetNameAndDesc(ganetId);
  345. generateIPRangeTable(netRanges);
  346. initNetDetails();
  347. renderMemeberTable(true);
  348. //Setup a listener to listen for member list change
  349. if (currentGANNetMemeberListener == undefined){
  350. currentGANNetMemeberListener = setInterval(function(){
  351. if ($('#networkMemeberTable').length > 0 && currentGANetID){
  352. renderMemeberTable();
  353. }
  354. }, 3000);
  355. }
  356. }
  357. //Exit point
  358. function exitToGanList(){
  359. $("#gan").load("./components/gan.html", function(){
  360. if (tabSwitchEventBind["gan"]){
  361. tabSwitchEventBind["gan"]();
  362. }
  363. });
  364. }
  365. </script>