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.
- ns2/__init__.py +0 -0
- ns2/core.py +0 -0
- ns2/dbus/__init__.py +0 -0
- ns2/dbus/dbus.py +13 -0
- ns2/lib/__init__.py +0 -0
- ns2/lib/accounts.py +130 -0
- ns2/lib/commands.py +32 -0
- ns2/lib/firewalld.py +167 -0
- ns2/lib/introspection/org.fedoraproject.FirewallD1.config.xml +3 -0
- ns2/lib/introspection/org.fedoraproject.FirewallD1.xml +763 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.AccessPoint.xml +106 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.AgentManager.xml +43 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Checkpoint.xml +36 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Connection.Active.xml +185 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.DHCP4Config.xml +21 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.DHCP6Config.xml +20 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Adsl.xml +21 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Bluetooth.xml +36 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Bond.xml +40 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Bridge.xml +41 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Dummy.xml +20 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Generic.xml +27 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Hsr.xml +51 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.IPTunnel.xml +107 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Infiniband.xml +31 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Ipvlan.xml +38 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Loopback.xml +8 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Lowpan.xml +27 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Macsec.xml +109 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Macvlan.xml +39 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Modem.xml +62 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.xml +34 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.OvsBridge.xml +21 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.OvsInterface.xml +11 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.OvsPort.xml +21 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Ppp.xml +11 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Statistics.xml +35 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Team.xml +48 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Tun.xml +65 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Veth.xml +18 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Vlan.xml +45 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Vrf.xml +18 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Vxlan.xml +139 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.WiMax.xml +109 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.WifiP2P.xml +76 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.WireGuard.xml +38 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Wired.xml +53 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Wireless.xml +131 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.Wpan.xml +20 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Device.xml +407 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.DnsManager.xml +40 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.IP4Config.xml +125 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.IP6Config.xml +95 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.PPP.xml +34 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.SecretAgent.xml +94 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Settings.Connection.xml +224 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.Settings.xml +233 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.VPN.Connection.xml +42 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.VPN.Plugin.xml +204 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.WiMax.Nsp.xml +35 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.WifiP2PPeer.xml +91 -0
- ns2/lib/introspection/org.freedesktop.NetworkManager.xml +598 -0
- ns2/lib/lib.py +0 -0
- ns2/lib/network_delay.py +92 -0
- ns2/lib/networking.py +528 -0
- ns2/lib/ntl.py +188 -0
- ns2/lib/pam_client.py +37 -0
- ns2/lib/ping_data_collector.py +37 -0
- ns2/lib/snmp.py +511 -0
- ns2/lib/socket.py +132 -0
- ns2/lib/socket_client.py +62 -0
- ns2/lib/systemd.py +151 -0
- ns2/lib/test.py +374 -0
- ns2/lib/udp_client.py +227 -0
- ns2/lib/udp_server.py +167 -0
- ns2/snmp/__init__.py +0 -0
- ns2/snmp/ns_dbus_service.py +38 -0
- ns2/snmp/pam_interface.py +19 -0
- ns2/snmp/snmp_interface.py +66 -0
- ns2/ui/__init__.py +0 -0
- ns2/ui/assets/NOVUS_LOGO.svg +105 -0
- ns2/ui/assets/favicon.png +0 -0
- ns2/ui/firewalld_page.py +375 -0
- ns2/ui/fpga_page.py +24 -0
- ns2/ui/login.py +65 -0
- ns2/ui/main.py +200 -0
- ns2/ui/networking_page.py +406 -0
- ns2/ui/ntp.py +105 -0
- ns2/ui/root.py +31 -0
- ns2/ui/snmp_page.py +353 -0
- ns2/ui/terminal.py +65 -0
- ns2/ui/tests_page.py +116 -0
- ns2/ui/theme.py +25 -0
- ns2/utils.py +5 -0
- ns2-0.2.6.dist-info/METADATA +78 -0
- ns2-0.2.6.dist-info/RECORD +98 -0
- ns2-0.2.6.dist-info/WHEEL +4 -0
- 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,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
|