ns2 0.2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. ns2/__init__.py +0 -0
  2. ns2/core.py +0 -0
  3. ns2/dbus/__init__.py +0 -0
  4. ns2/dbus/dbus.py +13 -0
  5. ns2/lib/__init__.py +0 -0
  6. ns2/lib/accounts.py +130 -0
  7. ns2/lib/commands.py +32 -0
  8. ns2/lib/firewalld.py +167 -0
  9. ns2/lib/introspection/org.fedoraproject.FirewallD1.config.xml +3 -0
  10. ns2/lib/introspection/org.fedoraproject.FirewallD1.xml +763 -0
  11. ns2/lib/introspection/org.freedesktop.NetworkManager.AccessPoint.xml +106 -0
  12. ns2/lib/introspection/org.freedesktop.NetworkManager.AgentManager.xml +43 -0
  13. ns2/lib/introspection/org.freedesktop.NetworkManager.Checkpoint.xml +36 -0
  14. ns2/lib/introspection/org.freedesktop.NetworkManager.Connection.Active.xml +185 -0
  15. ns2/lib/introspection/org.freedesktop.NetworkManager.DHCP4Config.xml +21 -0
  16. ns2/lib/introspection/org.freedesktop.NetworkManager.DHCP6Config.xml +20 -0
  17. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Adsl.xml +21 -0
  18. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Bluetooth.xml +36 -0
  19. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Bond.xml +40 -0
  20. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Bridge.xml +41 -0
  21. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Dummy.xml +20 -0
  22. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Generic.xml +27 -0
  23. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Hsr.xml +51 -0
  24. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.IPTunnel.xml +107 -0
  25. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Infiniband.xml +31 -0
  26. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Ipvlan.xml +38 -0
  27. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Loopback.xml +8 -0
  28. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Lowpan.xml +27 -0
  29. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Macsec.xml +109 -0
  30. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Macvlan.xml +39 -0
  31. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Modem.xml +62 -0
  32. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.xml +34 -0
  33. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml +21 -0
  34. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml +11 -0
  35. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml +21 -0
  36. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Ppp.xml +11 -0
  37. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Statistics.xml +35 -0
  38. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Team.xml +48 -0
  39. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Tun.xml +65 -0
  40. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Veth.xml +18 -0
  41. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Vlan.xml +45 -0
  42. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Vrf.xml +18 -0
  43. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Vxlan.xml +139 -0
  44. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.WiMax.xml +109 -0
  45. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.WifiP2P.xml +76 -0
  46. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.WireGuard.xml +38 -0
  47. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Wired.xml +53 -0
  48. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Wireless.xml +131 -0
  49. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Wpan.xml +20 -0
  50. ns2/lib/introspection/org.freedesktop.NetworkManager.Device.xml +407 -0
  51. ns2/lib/introspection/org.freedesktop.NetworkManager.DnsManager.xml +40 -0
  52. ns2/lib/introspection/org.freedesktop.NetworkManager.IP4Config.xml +125 -0
  53. ns2/lib/introspection/org.freedesktop.NetworkManager.IP6Config.xml +95 -0
  54. ns2/lib/introspection/org.freedesktop.NetworkManager.PPP.xml +34 -0
  55. ns2/lib/introspection/org.freedesktop.NetworkManager.SecretAgent.xml +94 -0
  56. ns2/lib/introspection/org.freedesktop.NetworkManager.Settings.Connection.xml +224 -0
  57. ns2/lib/introspection/org.freedesktop.NetworkManager.Settings.xml +233 -0
  58. ns2/lib/introspection/org.freedesktop.NetworkManager.VPN.Connection.xml +42 -0
  59. ns2/lib/introspection/org.freedesktop.NetworkManager.VPN.Plugin.xml +204 -0
  60. ns2/lib/introspection/org.freedesktop.NetworkManager.WiMax.Nsp.xml +35 -0
  61. ns2/lib/introspection/org.freedesktop.NetworkManager.WifiP2PPeer.xml +91 -0
  62. ns2/lib/introspection/org.freedesktop.NetworkManager.xml +598 -0
  63. ns2/lib/lib.py +0 -0
  64. ns2/lib/network_delay.py +92 -0
  65. ns2/lib/networking.py +528 -0
  66. ns2/lib/ntl.py +188 -0
  67. ns2/lib/pam_client.py +37 -0
  68. ns2/lib/ping_data_collector.py +37 -0
  69. ns2/lib/snmp.py +511 -0
  70. ns2/lib/socket.py +132 -0
  71. ns2/lib/socket_client.py +62 -0
  72. ns2/lib/systemd.py +151 -0
  73. ns2/lib/test.py +374 -0
  74. ns2/lib/udp_client.py +227 -0
  75. ns2/lib/udp_server.py +167 -0
  76. ns2/snmp/__init__.py +0 -0
  77. ns2/snmp/ns_dbus_service.py +38 -0
  78. ns2/snmp/pam_interface.py +19 -0
  79. ns2/snmp/snmp_interface.py +66 -0
  80. ns2/ui/__init__.py +0 -0
  81. ns2/ui/assets/NOVUS_LOGO.svg +105 -0
  82. ns2/ui/assets/favicon.png +0 -0
  83. ns2/ui/firewalld_page.py +375 -0
  84. ns2/ui/fpga_page.py +24 -0
  85. ns2/ui/login.py +65 -0
  86. ns2/ui/main.py +200 -0
  87. ns2/ui/networking_page.py +406 -0
  88. ns2/ui/ntp.py +105 -0
  89. ns2/ui/root.py +31 -0
  90. ns2/ui/snmp_page.py +353 -0
  91. ns2/ui/terminal.py +65 -0
  92. ns2/ui/tests_page.py +116 -0
  93. ns2/ui/theme.py +25 -0
  94. ns2/utils.py +5 -0
  95. ns2-0.2.6.dist-info/METADATA +78 -0
  96. ns2-0.2.6.dist-info/RECORD +98 -0
  97. ns2-0.2.6.dist-info/WHEEL +4 -0
  98. ns2-0.2.6.dist-info/entry_points.txt +3 -0
ns2/ui/snmp_page.py ADDED
@@ -0,0 +1,353 @@
1
+ from ns.lib.systemd import *
2
+
3
+ from nicegui import ui, app
4
+ from dataclasses import dataclass, asdict
5
+
6
+ from ns.lib.commands import runCmd
7
+
8
+ from typing import Optional
9
+
10
+ from ns.dbus import dbus
11
+ from ns.lib.snmp import GetSnmp, snmp_call,V3User, V2User, snmp_config_file, default_persistent_dir_path
12
+
13
+
14
+
15
+ sourceValidation = {"Please enter a valid ip address, network or default": lambda value: len(value) > 0}
16
+ passphraseValidation = {"Passphrase must be at least 8 characters": lambda value: len(value) >= 8,
17
+ "Passphrase must be 24 or less characaters": lambda value: 24 >= len(value)}
18
+ usernameValidation = {"Username must be at least 5 characters": lambda value: len(value) >= 5,
19
+ "Username must be 24 or less characaters": lambda value: 24 >= len(value)}
20
+
21
+ def validate_group(group: list):
22
+ return [x.validate() for x in group]
23
+
24
+
25
+ async def create_v3_user_dialog():
26
+
27
+ with ui.dialog() as createV3Dialog:
28
+ v3 = V3User()
29
+ with ui.card().classes("w-full"):
30
+ with ui.column().classes("w-full"):
31
+ version = ui.input(label="Version").classes("w-full").bind_value(v3, "Version")
32
+ version.disable()
33
+ username = ui.input(label="Username", validation=usernameValidation).classes("w-full").bind_value(v3, "UserName")
34
+ permissions = ui.select(label="Permissions", options=["roprivgroup", "rwprivgroup"]).classes("w-full").bind_value(v3, "Permissions")
35
+ auth_type = ui.select(label="Auth Alg", options=['SHA', 'MD5']).classes("w-full").bind_value(v3, "AuthType")
36
+ auth_pass = ui.input(label="Auth Passphrase", validation=passphraseValidation).classes("w-full").bind_value(v3, "AuthPassphrase")
37
+ priv_type = ui.select(label="Priv Alg", options=["AES", "DES"]).classes("w-full").bind_value(v3, "PrivType")
38
+ priv_pass = ui.input(label="Auth Passphrase", validation=passphraseValidation).classes("w-full").bind_value(v3, "PrivPassphrase")
39
+ with ui.row().classes("items-center justify-between gap-4 w-full"):
40
+
41
+ async def on_save_cb():
42
+ if all(validate_group([version, username, permissions, auth_type, auth_pass, priv_type, priv_pass])):
43
+ print(asdict(v3))
44
+
45
+ snmp = await GetSnmp(dbus.AppBus)
46
+ rsp = await snmp.call_create_v3_user(asdict(v3))
47
+ print(rsp)
48
+ await v3table.refresh()
49
+ createV3Dialog.close()
50
+ else:
51
+ ui.notify("Please correct the errors", type='negative')
52
+
53
+ def on_cancel_cb():
54
+ createV3Dialog.close()
55
+
56
+ ui.button("save", on_click=on_save_cb).props("flat color=accent align=left")
57
+ ui.button(icon="cancel", on_click=on_cancel_cb).props("flat color=accent align=left")
58
+
59
+ return createV3Dialog
60
+
61
+
62
+ @ui.refreshable
63
+ async def v3table():
64
+ snmp = await GetSnmp(dbus.AppBus)
65
+ v3Users = await snmp.call_get_v3_users()
66
+ print(v3Users)
67
+ createV3Dialog = await create_v3_user_dialog()
68
+
69
+ table = ui.table(
70
+ title="v3 Users",
71
+ rows=v3Users,
72
+ column_defaults={
73
+ "align": "left",
74
+ "headerClasses": "uppercase text-primary",
75
+ },
76
+ ).classes("w-full")
77
+
78
+ table.add_slot(f'body-cell-UserName', f''' <q-td :props="props">
79
+ <a :href="'/snmp/v3/'+ props.row.UserName" class="text-accent cursor-pointer hover:underline"> {{{{ props.value }}}} </a>
80
+ </q-td> ''')
81
+
82
+ table.props(f'visible-columns={"UserName,Version,GroupName,AuthType,PrivType"}') # Only show these
83
+
84
+ with table.add_slot('top-right'):
85
+ ui.button(icon="add", on_click = createV3Dialog.open).props(
86
+ "flat color=accent align=left").classes("w-full").props("dense")
87
+
88
+
89
+
90
+
91
+
92
+ @ui.refreshable
93
+ async def v2table():
94
+ snmp = await GetSnmp(dbus.AppBus)
95
+ v2Users = await snmp.call_get_v2_users()
96
+
97
+ with ui.dialog() as createV2Dialog:
98
+ v2 = V2User()
99
+ v2.Version = "v2c"
100
+ v2.Permissions = "rwnoauthgroup"
101
+ v2.Source = "default"
102
+ with ui.card().classes("w-full"):
103
+ with ui.column().classes("w-full"):
104
+ version = ui.select(label="Version", options=["v2c", "v1"]).classes("w-full").bind_value(v2, "Version")
105
+ permissions = ui.select(label="Permissions", options=['rwnoauthgroup', 'ronoauthgroup']).classes("w-full").bind_value(v2, "Permissions")
106
+ community = ui.input("Community",
107
+ validation={'Community required': lambda value: len(value) > 0}).classes("w-full").bind_value(v2, "Community")
108
+ source = ui.input("Source / IP Address",
109
+ validation=sourceValidation).classes("w-full").bind_value(v2, "Source")
110
+ with ui.row().classes("items-center justify-between gap-4 w-full"):
111
+
112
+ async def on_save_cb():
113
+ if all(validate_group([version, permissions, community, source])):
114
+ await snmp.call_create_v2_user(asdict(v2))
115
+ await v2table.refresh()
116
+ createV2Dialog.close()
117
+ else:
118
+ ui.notify("Please correct the errors", type='negative')
119
+
120
+ def on_cancel_cb():
121
+ createV2Dialog.close()
122
+ ui.button("save", on_click=on_save_cb).props("flat color=accent align=left")
123
+ ui.button(icon="cancel", on_click=on_cancel_cb).props("flat color=accent align=left")
124
+
125
+ table = ui.table(
126
+ title="v2 Users",
127
+ rows=v2Users,
128
+ column_defaults={
129
+ "align": "left",
130
+ "headerClasses": "uppercase text-primary",
131
+ },
132
+ ).classes("w-full")
133
+
134
+ table.add_slot(f'body-cell-Community', f'''
135
+ <q-td :props="props">
136
+ <a :href="'/snmp/v2/'+ props.row.Community" class="text-accent cursor-pointer hover:underline"> {{{{ props.value }}}} </a>
137
+ </q-td>
138
+ ''')
139
+
140
+ table.props(f'visible-columns={"Community,Version,Source,GroupName"}') # Only show these
141
+
142
+ with table.add_slot('top-right'):
143
+ ui.button(icon="add", on_click = createV2Dialog.open).props(
144
+ "flat color=accent align=left").classes("w-full").props("dense")
145
+
146
+
147
+
148
+ async def snmp_status():
149
+ with ui.column() as status:
150
+ ui.label("SNMP").classes("text-h5")
151
+
152
+ async def snmp_switch_cb(e):
153
+ action = "enable" if e.sender.value else "disable"
154
+ with ui.dialog() as dialog, ui.card():
155
+ ui.label(f'Are you sure you want to {action} snmp?')
156
+ with ui.row():
157
+ ui.button('Cancel', on_click=lambda: dialog.submit("Cancel")).props("flat color=accent align=left")
158
+ ui.button(f'{action}', on_click=lambda: dialog.submit(action)).props("flat color=accent align=left")
159
+
160
+ result = await dialog
161
+ active = await isActive(dbus.AppBus, 'snmpd.service')
162
+
163
+
164
+ if result == "enable" and not active:
165
+ await systemd_start(dbus.AppBus, 'snmpd.service')
166
+
167
+ if result == "disable" and active:
168
+ await systemd_stop(dbus.AppBus, 'snmpd.service')
169
+
170
+
171
+ e.sender.value = await isActive(dbus.AppBus, 'snmpd.service')
172
+
173
+ async def snmp_reset_cb(e):
174
+ with ui.dialog() as dialog, ui.card():
175
+ ui.label(f'Are you sure you want to reset snmp?')
176
+ with ui.row():
177
+ ui.button('Cancel', on_click=lambda: dialog.submit("Cancel")).props("flat color=accent align=left")
178
+ ui.button('Reset', on_click=lambda: dialog.submit("reset")).props("flat color=accent align=left")
179
+ if await dialog == "reset":
180
+
181
+ snmp = await GetSnmp(dbus.AppBus)
182
+ await snmp.call_reset()
183
+ v2table.refresh()
184
+ v3table.refresh()
185
+
186
+
187
+ with ui.card().classes("w-full"):
188
+ snmp_service_switch = ui.switch("SNMPD Status").on('click', lambda e: snmp_switch_cb(e)).props("flat color=accent align=left dense")
189
+ snmp_service_switch.value = await isActive(dbus.AppBus, 'snmpd.service')
190
+ ui.button("Reset SNMPD Config", on_click=snmp_reset_cb).props("flat color=accent align=left dense")
191
+
192
+ return status
193
+
194
+ async def snmp_page():
195
+
196
+ #def props_cb(interface_name, changed_properties, invalidated_properties):
197
+ # print("props ch cb")
198
+ # #print(changed_properties['ActiveState'])
199
+ # status.update()
200
+ #
201
+ #print("registered")
202
+ #snmpDaemon = await getUnitInterface(dbus.AppBus, "snmpd.service")
203
+ #snmpDaemon.on_properties_changed(props_cb)
204
+
205
+
206
+ await snmp_status()
207
+ await v2table() # Only show these
208
+ await v3table()
209
+
210
+
211
+
212
+ async def snmp_user_page(version :str, user: str):
213
+ with ui.row():
214
+ ui.link('SNMP', '/snmp')
215
+ ui.label('>')
216
+ ui.label(user)
217
+
218
+ if version == "v2":
219
+ await edit_delete_v2_user_card(user)
220
+ if version == "v3":
221
+ await edit_delete_v3_user_card(user)
222
+
223
+
224
+
225
+ def enable_group(fields):
226
+ for f in fields:
227
+ f.enabled = True
228
+
229
+ def disable_group(fields):
230
+ for f in fields:
231
+ f.enabled = False
232
+
233
+
234
+
235
+
236
+ async def edit_delete_v2_user_card(community):
237
+ snmp = await GetSnmp(dbus.Bus)
238
+ user = await snmp.call_get_v2_user_by_community(community)
239
+ v2User = V2User(**user)
240
+ with ui.card().classes("w-full"):
241
+ with ui.column().classes("w-full"):
242
+ version = ui.select(label="Version", options=["v2c", "v1"]).classes("w-full").bind_value(v2User, "Version")
243
+ permissions = ui.select(label="Permissions", options=['rwnoauthgroup', 'ronoauthgroup']).classes("w-full").bind_value(v2User, "Permissions")
244
+ community = ui.input("Community", validation={'Community required': lambda value: len(value) > 0}).classes("w-full").bind_value(v2User, "Community")
245
+ source = ui.input("Source / IP Address", validation=sourceValidation).classes("w-full").bind_value(v2User, "Source")
246
+ with ui.row().classes("items-center justify-between gap-4 w-full"):
247
+
248
+ async def on_save_cb():
249
+ disable_group(group)
250
+ save_button.enabled = False
251
+ edit_button.enabled = True
252
+ await snmp.call_modify_v2_user(asdict(v2User))
253
+ await v2table.refresh()
254
+ ui.navigate.back()
255
+
256
+ def on_edit_cb():
257
+ enable_group(group)
258
+ edit_button.enabled = False
259
+ save_button.enabled = True
260
+
261
+ async def on_delete_cb():
262
+ with ui.dialog() as dialog, ui.card():
263
+ ui.label(f'Are you sure you want to delete {v2User.Community}?')
264
+ with ui.row():
265
+ ui.button('Yes', on_click=lambda: dialog.submit(True)).props("flat color=accent align=left")
266
+ ui.button('No', on_click=lambda: dialog.submit(False)).props("flat color=accent align=left")
267
+ result = await dialog
268
+ if result:
269
+ await snmp.call_remove_v2_user(asdict(v2User))
270
+ v2table.refresh()
271
+ ui.navigate.back()
272
+ ui.notify(f'User {v2User.Community} deleted...')
273
+ else:
274
+ dialog.close()
275
+
276
+ edit_button = ui.button("edit", on_click= on_edit_cb).props("flat color=accent align=left")
277
+ save_button = ui.button("save", on_click= on_save_cb).props("flat color=accent align=left")
278
+ delete_button = ui.button(icon="delete", on_click=on_delete_cb).props("flat color=accent align=left")
279
+
280
+ group = [community, source, version, permissions]
281
+
282
+ disable_group(group)
283
+ edit_button.enabled = True
284
+ save_button.enabled = False
285
+
286
+
287
+
288
+
289
+
290
+
291
+ async def edit_delete_v3_user_card(username):
292
+ snmp = await GetSnmp(dbus.Bus)
293
+ userData = await snmp.call_get_v3_user_by_username(username)
294
+ initUser = V3User(**userData)
295
+ finalUser = V3User(**userData)
296
+
297
+ with ui.card().classes("w-full"):
298
+ with ui.column().classes("w-full"):
299
+
300
+ version = ui.input(label="Version").classes("w-full").bind_value(finalUser, "Version")
301
+ version.disable()
302
+ username = ui.input(label="Username", validation=usernameValidation).classes("w-full").bind_value(finalUser, "UserName")
303
+ permissions = ui.select(label="Permissions", options=["roprivgroup", "rwprivgroup"]).classes("w-full").bind_value(finalUser, "Permissions")
304
+ auth_type = ui.select(label="Auth Alg", options=['SHA', 'MD5']).classes("w-full").bind_value(finalUser, "AuthType")
305
+ auth_pass = ui.input(label="Auth Passphrase", validation=passphraseValidation).classes("w-full").bind_value(finalUser, "AuthPassphrase")
306
+ priv_type = ui.select(label="Priv Alg", options=["AES", "DES"]).classes("w-full").bind_value(finalUser, "PrivType")
307
+ priv_pass = ui.input(label="Auth Passphrase", validation=passphraseValidation).classes("w-full").bind_value(finalUser, "PrivPassphrase")
308
+
309
+ with ui.row().classes("items-center justify-between gap-4 w-full"):
310
+
311
+ async def on_save_cb():
312
+ disable_group(group)
313
+ save_button.enabled = False
314
+ edit_button.enabled = True
315
+ if all(validate_group([version, username, permissions, auth_type, auth_pass, priv_type, priv_pass])):
316
+ await snmp.call_modify_v3_user(asdict(initUser), asdict(finalUser))
317
+ #EditV3User(inituser, finaluser)
318
+ ui.navigate.back()
319
+ else:
320
+ ui.notify("Please correct the errors", type='negative')
321
+
322
+
323
+ def on_edit_cb():
324
+ enable_group(group)
325
+ edit_button.enabled = False
326
+ save_button.enabled = True
327
+
328
+
329
+ async def on_delete_cb():
330
+ with ui.dialog() as dialog, ui.card():
331
+ ui.label(f'Are you sure you want to delete {initUser.UserName}?')
332
+ with ui.row():
333
+ ui.button('Yes', on_click=lambda: dialog.submit(True)).props("flat color=accent align=left")
334
+ ui.button('No', on_click=lambda: dialog.submit(False)).props("flat color=accent align=left")
335
+ result = await dialog
336
+ if result:
337
+ await snmp.call_remove_v3_user(asdict(initUser))
338
+ ui.navigate.back()
339
+ ui.notify(f'User {initUser.UserName} deleted...')
340
+ else:
341
+ dialog.close()
342
+
343
+ edit_button = ui.button("edit", on_click= on_edit_cb).props("flat color=accent align=left")
344
+ save_button = ui.button("save", on_click= on_save_cb).props("flat color=accent align=left")
345
+ delete_button = ui.button(icon="delete", on_click=on_delete_cb).props("flat color=accent align=left")
346
+
347
+ group = [permissions, username, auth_type,auth_pass,priv_type,priv_pass]
348
+
349
+ disable_group(group)
350
+ edit_button.enabled = True
351
+ save_button.enabled = False
352
+
353
+
ns2/ui/terminal.py ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env python3
2
+ """Example for connecting a `Xterm` element with a `Bash` process running in a pty.
3
+
4
+ WARNING: This example gives the clients full access to the server through Bash. Use with caution!
5
+ """
6
+ import os
7
+ import pty
8
+ import signal
9
+ from functools import partial
10
+ import time
11
+ from nicegui import core, events, ui
12
+
13
+ pty_fd = None
14
+ pty_pid = None
15
+
16
+ isOpen = False
17
+
18
+ # if terminal is open, dont do anything
19
+ # if terminal is closed, open it
20
+
21
+ def terminal_page():
22
+ global pty_fd, pty_pid, isOpen
23
+
24
+ if isOpen:
25
+ ui.label("terminal in use")
26
+ os.close(pty_fd)
27
+ os.kill(pty_pid, signal.SIGKILL)
28
+ print('Terminal closed')
29
+ isOpen = False
30
+
31
+ else:
32
+
33
+ pty_pid, pty_fd = pty.fork() # create a new pseudo-terminal (pty) fork of the process
34
+ if pty_pid == pty.CHILD:
35
+ os.execv('/bin/bash', ('bash',)) # child process of the fork gets replaced with "bash"
36
+ isOpen = True
37
+ print('Terminal opened')
38
+
39
+ terminal = ui.xterm()
40
+
41
+ @partial(core.loop.add_reader, pty_fd)
42
+ def pty_to_terminal():
43
+ try:
44
+ data = os.read(pty_fd, 1024)
45
+ except OSError:
46
+ print('Stopping reading from pty') # error reading the pty; probably bash was exited
47
+ core.loop.remove_reader(pty_fd)
48
+ else:
49
+ terminal.write(data)
50
+ @terminal.on_data
51
+ def terminal_to_pty(event: events.XtermDataEventArguments):
52
+ try:
53
+ os.write(pty_fd, event.data.encode('utf-8'))
54
+ except OSError:
55
+ pass # error writing to the pty; probably bash was exited
56
+
57
+ @ui.context.client.on_disconnect
58
+ def kill_bash():
59
+ global isOpen
60
+ if isOpen:
61
+ os.close(pty_fd)
62
+ os.kill(pty_pid, signal.SIGKILL)
63
+ print('Terminal closed')
64
+ isOpen = False
65
+
ns2/ui/tests_page.py ADDED
@@ -0,0 +1,116 @@
1
+
2
+ from nicegui import ui, app, background_tasks, events
3
+
4
+ import pandas as pd
5
+ import numpy as np
6
+
7
+ import plotly.graph_objects as go
8
+ import plotly.express as px
9
+
10
+ import plotly.io as pio
11
+ pio.templates.default = "plotly_dark"
12
+
13
+ from ns.lib.network_delay import get_network_delay_data_locally, calculate_last_50_jitter, get_files_to_view, get_data_by_name
14
+
15
+
16
+
17
+ def set_selected_file(f):
18
+ global selected_file
19
+ selected_file = f
20
+
21
+ async def tests_page():
22
+ global selected_file
23
+
24
+ try:
25
+ selected_file = get_files_to_view()[0]['href']
26
+ except Exception as e:
27
+ print(e)
28
+
29
+ with ui.column():
30
+ with ui.row() as pageContainer:
31
+ ui.label("Network Delay Test Data").classes("text-h5")
32
+
33
+ with ui.column():
34
+ for r in get_files_to_view():
35
+ ui.button(r["href"], on_click=lambda f=r['href']: set_selected_file(f)).classes('bg-accent')
36
+
37
+ with ui.row():
38
+
39
+ def build_plot():
40
+
41
+ #start, ts, delays = get_network_delay_data_locally()
42
+
43
+ start, ts, delays = get_data_by_name(selected_file)
44
+ #delays = [0]
45
+ #start = ''
46
+
47
+ f1 = go.Figure(go.Scatter(x=list(range(len(delays))),y=delays))
48
+ f2 = go.Figure(px.histogram(x=delays))
49
+ jitter = calculate_last_50_jitter(delays)
50
+ f1.update_xaxes(title_text=f"Samples - Jitter on last 50: {jitter}")
51
+ f1.update_yaxes(title_text="RTT (Delay) (ms)")
52
+
53
+ f1.update_layout(title_text=f"Ping Over VPN Test Started: {start}")
54
+
55
+ mean = np.mean(delays)
56
+ std = np.std(delays)
57
+ f2.update_layout(title_text=f"Mean: {mean} STD: {std}")
58
+
59
+ #fig.update_layout(title_text="Pinging home server from novus",margin=dict(l=0, r=0, t=0, b=0))
60
+
61
+ p1 = ui.plotly(f1).classes("w-full")
62
+ p2 = ui.plotly(f2).classes("w-full")
63
+
64
+ return f1, p1, f2 , p2
65
+ f1, p1, f2, p2 = build_plot()
66
+
67
+
68
+ def update_plots():
69
+ start, ts, delays = get_data_by_name(selected_file)
70
+ #_, ts, delays = get_network_delay_data_locally()
71
+ jitter = calculate_last_50_jitter(delays)
72
+ f1.update_layout(title_text=f"Ping Over VPN Test Started: {start}")
73
+
74
+ f1.update_xaxes(title_text=f"Samples - Jitter on last 50: {jitter}")
75
+ f1.data[0].x = list(range(len(delays)))
76
+ f1.data[0].y = delays
77
+
78
+ f2.data[0].x = delays
79
+
80
+ p1.update()
81
+ p2.update()
82
+ mean = np.mean(delays)
83
+ std = np.std(delays)
84
+ f2.update_layout(title_text=f"Mean: {mean} STD: {std}")
85
+
86
+
87
+
88
+ timer = ui.timer(5, callback=update_plots)
89
+
90
+ def toggle_timer():
91
+ if timer.active:
92
+ timer.deactivate()
93
+ else:
94
+ timer.activate()
95
+
96
+
97
+ def get_down_latest():
98
+
99
+ _, ts, delays = get_data_by_name(selected_file)
100
+
101
+ data = {"ts": ts, "rtt": delays}
102
+
103
+ df = pd.DataFrame(data)
104
+
105
+ df.to_excel("temp.xlsx", index=False, header=True)
106
+
107
+ ui.download.file('temp.xlsx')
108
+
109
+
110
+ ui.button("Toggle Refresh", on_click=toggle_timer).classes("bg-accent")
111
+
112
+ ui.button("Download selected", on_click=get_down_latest ).classes("bg-accent")
113
+
114
+
115
+
116
+
ns2/ui/theme.py ADDED
@@ -0,0 +1,25 @@
1
+ from nicegui import ui, app
2
+
3
+
4
+ def init_colors():
5
+ ui.colors(
6
+ primary="#ffffff",
7
+ secondary="#191a1a",
8
+ accent="#ff0000",
9
+ dark="#1f2121",
10
+ dark_page="#121212",
11
+ positive="#21ba45",
12
+ negative="#c10015",
13
+ info="#31ccec",
14
+ warning="#f2c037",
15
+ firewallcard="#3A3D3D")
16
+
17
+ dark = ui.dark_mode()
18
+ dark.enable()
19
+ ui.query("body").classes("bg-secondary")
20
+
21
+
22
+
23
+ # background #191a1a
24
+
25
+ # menu color #1f2121
ns2/utils.py ADDED
@@ -0,0 +1,5 @@
1
+ from importlib.resources import files
2
+
3
+ # These return Traversable objects that work like Path objects
4
+ ASSETS_DIR = files('ns').joinpath('ui').joinpath('assets')
5
+ INTROSPECTION_DIR = files('ns').joinpath('lib').joinpath('introspection')
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: ns2
3
+ Version: 0.2.6
4
+ Summary: Novus Power Products server configuration tool
5
+ License: MIT
6
+ Author: jowens25
7
+ Author-email: jowens@novuspower.com
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Dist: aiofiles
17
+ Requires-Dist: aiohappyeyeballs
18
+ Requires-Dist: aiohttp
19
+ Requires-Dist: aiosignal
20
+ Requires-Dist: annotated-doc
21
+ Requires-Dist: annotated-types
22
+ Requires-Dist: anyio
23
+ Requires-Dist: attrs
24
+ Requires-Dist: beautifulsoup4
25
+ Requires-Dist: bidict
26
+ Requires-Dist: bs4
27
+ Requires-Dist: certifi
28
+ Requires-Dist: charset-normalizer
29
+ Requires-Dist: click
30
+ Requires-Dist: dbus-next
31
+ Requires-Dist: docutils
32
+ Requires-Dist: fastapi
33
+ Requires-Dist: frozenlist
34
+ Requires-Dist: h11
35
+ Requires-Dist: httpcore
36
+ Requires-Dist: httptools
37
+ Requires-Dist: httpx
38
+ Requires-Dist: idna
39
+ Requires-Dist: ifaddr
40
+ Requires-Dist: itsdangerous
41
+ Requires-Dist: jinja2
42
+ Requires-Dist: markdown2
43
+ Requires-Dist: markupsafe
44
+ Requires-Dist: multidict
45
+ Requires-Dist: narwhals
46
+ Requires-Dist: nicegui
47
+ Requires-Dist: numpy
48
+ Requires-Dist: orjson
49
+ Requires-Dist: packaging
50
+ Requires-Dist: pam
51
+ Requires-Dist: pandas
52
+ Requires-Dist: plotly
53
+ Requires-Dist: propcache
54
+ Requires-Dist: pydantic
55
+ Requires-Dist: pydantic-core
56
+ Requires-Dist: pygments
57
+ Requires-Dist: pyinstaller
58
+ Requires-Dist: python-dateutil
59
+ Requires-Dist: python-dotenv
60
+ Requires-Dist: python-engineio
61
+ Requires-Dist: python-multipart
62
+ Requires-Dist: python-pam
63
+ Requires-Dist: python-socketio
64
+ Requires-Dist: pyyaml
65
+ Requires-Dist: requests
66
+ Requires-Dist: simple-websocket
67
+ Requires-Dist: six
68
+ Requires-Dist: soupsieve
69
+ Requires-Dist: typing-extensions
70
+ Requires-Dist: typing-inspection
71
+ Requires-Dist: urllib3
72
+ Requires-Dist: uvicorn
73
+ Requires-Dist: uvloop
74
+ Requires-Dist: watchfiles
75
+ Requires-Dist: websockets
76
+ Requires-Dist: wheel2deb
77
+ Requires-Dist: wsproto
78
+ Requires-Dist: yarl