db4e 0.16.0__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.
- db4e/App.py +168 -0
- db4e/Db4E.tcss +104 -0
- db4e/Messages/NavLeafSelected.py +21 -0
- db4e/Messages/RefreshNavPane.py +19 -0
- db4e/Messages/SubmitFormData.py +19 -0
- db4e/Messages/SwitchPane.py +21 -0
- db4e/Messages/UpdateTopBar.py +20 -0
- db4e/Modules/ConfigMgr.py +112 -0
- db4e/Modules/DbMgr.py +94 -0
- db4e/Modules/DeploymentMgr.py +74 -0
- db4e/Modules/InstallMgr.py +272 -0
- db4e/Modules/PaneCatalogue.py +37 -0
- db4e/Modules/PaneMgr.py +67 -0
- db4e/Modules/__init__.py +0 -0
- db4e/Panes/Db4E.py +73 -0
- db4e/Panes/InitialSetup.py +70 -0
- db4e/Panes/InstallResults.py +36 -0
- db4e/Panes/Welcome.py +20 -0
- db4e/Templates/__init__.py +0 -0
- db4e/Templates/db/Deployment.py +122 -0
- db4e/Templates/db4e/systemd/db4e.service +18 -0
- db4e/Templates/monerod-0.18.4.0/bin/monerod +0 -0
- db4e/Templates/monerod-0.18.4.0/bin/start-monerod.sh +64 -0
- db4e/Templates/monerod-0.18.4.0/conf/monerod.ini +34 -0
- db4e/Templates/monerod-0.18.4.0/systemd/monerod@.service +21 -0
- db4e/Templates/monerod-0.18.4.0/systemd/monerod@.socket +13 -0
- db4e/Templates/p2pool-4.8/bin/p2pool +0 -0
- db4e/Templates/p2pool-4.8/bin/start-p2pool.sh +67 -0
- db4e/Templates/p2pool-4.8/conf/p2pool.ini +24 -0
- db4e/Templates/p2pool-4.8/systemd/p2pool@.service +19 -0
- db4e/Templates/p2pool-4.8/systemd/p2pool@.socket +12 -0
- db4e/Templates/xmrig-6.23.0/bin/xmrig +0 -0
- db4e/Templates/xmrig-6.23.0/conf/config.json +133 -0
- db4e/Templates/xmrig-6.23.0/systemd/xmrig@.service +18 -0
- db4e/Widgets/Clock.py +32 -0
- db4e/Widgets/DetailPane.py +38 -0
- db4e/Widgets/FormButton.py +21 -0
- db4e/Widgets/NavPane.py +58 -0
- db4e/Widgets/TopBar.py +45 -0
- db4e/Widgets/__init__.py +0 -0
- db4e/bin/db4e-backup.sh +90 -0
- db4e/bin/db4e-initial-setup.sh +85 -0
- db4e/bin/db4e-install-service.sh +43 -0
- db4e/bin/db4e-metrics.sh +47 -0
- db4e/bin/db4e-uninstall-service.sh +49 -0
- db4e-0.16.0.dist-info/LICENSE +674 -0
- db4e-0.16.0.dist-info/METADATA +31 -0
- db4e-0.16.0.dist-info/RECORD +50 -0
- db4e-0.16.0.dist-info/WHEEL +4 -0
- db4e-0.16.0.dist-info/entry_points.txt +3 -0
db4e/App.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# db4e/App.py
|
|
4
|
+
|
|
5
|
+
# Database 4 Everything
|
|
6
|
+
# Author: Nadim-Daniel Ghaznavi
|
|
7
|
+
# Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
8
|
+
# License: GPL 3.0
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
from dataclasses import dataclass, field, fields
|
|
13
|
+
from importlib import metadata
|
|
14
|
+
from textual.app import App
|
|
15
|
+
from textual.theme import Theme as TextualTheme
|
|
16
|
+
from textual.widgets import Footer
|
|
17
|
+
from textual.containers import Vertical
|
|
18
|
+
from rich.theme import Theme as RichTheme
|
|
19
|
+
from rich.traceback import Traceback
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
__package_name__ = metadata.metadata(__package__ or __name__)["Name"]
|
|
23
|
+
__version__ = metadata.version(__package__ or __name__)
|
|
24
|
+
except Exception:
|
|
25
|
+
__package_name__ = "Db4E"
|
|
26
|
+
__version__ = "N/A"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
from db4e.Widgets.TopBar import TopBar
|
|
30
|
+
from db4e.Widgets.Clock import Clock
|
|
31
|
+
from db4e.Widgets.DetailPane import DetailPane
|
|
32
|
+
from db4e.Widgets.NavPane import NavPane
|
|
33
|
+
from db4e.Modules.ConfigMgr import ConfigMgr, Config
|
|
34
|
+
from db4e.Modules.DeploymentMgr import DeploymentMgr
|
|
35
|
+
from db4e.Modules.PaneCatalogue import PaneCatalogue
|
|
36
|
+
from db4e.Modules.PaneMgr import PaneMgr
|
|
37
|
+
from db4e.Modules.InstallMgr import InstallMgr
|
|
38
|
+
from db4e.Messages.SubmitFormData import SubmitFormData
|
|
39
|
+
from db4e.Messages.SwitchPane import SwitchPane
|
|
40
|
+
from db4e.Messages.UpdateTopBar import UpdateTopBar
|
|
41
|
+
from db4e.Messages.RefreshNavPane import RefreshNavPane
|
|
42
|
+
from db4e.Messages.NavLeafSelected import NavLeafSelected
|
|
43
|
+
|
|
44
|
+
RICH_THEME =RichTheme(
|
|
45
|
+
{
|
|
46
|
+
"white": "#e9e9e9",
|
|
47
|
+
"green": "#54efae",
|
|
48
|
+
"yellow": "#f6ff8f",
|
|
49
|
+
"dark_yellow": "#e6d733",
|
|
50
|
+
"red": "#fd8383",
|
|
51
|
+
"purple": "#b565f3",
|
|
52
|
+
"dark_gray": "#969aad",
|
|
53
|
+
"b dark_gray": "b #969aad",
|
|
54
|
+
"highlight": "#91abec",
|
|
55
|
+
"label": "#c5c7d2",
|
|
56
|
+
"b label": "b #c5c7d2",
|
|
57
|
+
"light_blue": "#bbc8e8",
|
|
58
|
+
"b white": "b #e9e9e9",
|
|
59
|
+
"b highlight": "b #91abec",
|
|
60
|
+
"b light_blue": "b #bbc8e8",
|
|
61
|
+
"recording": "#ff5e5e",
|
|
62
|
+
"b recording": "b #ff5e5e",
|
|
63
|
+
"panel_border": "#6171a6",
|
|
64
|
+
"table_border": "#333f62",
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
TEXTUAL_THEME = TextualTheme(
|
|
68
|
+
name="custom",
|
|
69
|
+
primary="white",
|
|
70
|
+
variables={
|
|
71
|
+
"white": "#e9e9e9",
|
|
72
|
+
"green": "#54efae",
|
|
73
|
+
"yellow": "#f6ff8f",
|
|
74
|
+
"dark_yellow": "#e6d733",
|
|
75
|
+
"red": "#fd8383",
|
|
76
|
+
"purple": "#b565f3",
|
|
77
|
+
"dark_gray": "#969aad",
|
|
78
|
+
"b_dark_gray": "b #969aad",
|
|
79
|
+
"highlight": "#91abec",
|
|
80
|
+
"label": "#c5c7d2",
|
|
81
|
+
"b_label": "b #c5c7d2",
|
|
82
|
+
"light_blue": "#bbc8e8",
|
|
83
|
+
"b_white": "b #e9e9e9",
|
|
84
|
+
"b_highlight": "b #91abec",
|
|
85
|
+
"b_light_blue": "b #bbc8e8",
|
|
86
|
+
"recording": "#ff5e5e",
|
|
87
|
+
"b_recording": "b #ff5e5e",
|
|
88
|
+
"panel_border": "#6171a6",
|
|
89
|
+
"table_border": "#333f62",
|
|
90
|
+
},
|
|
91
|
+
)
|
|
92
|
+
class Db4EApp(App):
|
|
93
|
+
TITLE = "Db4E"
|
|
94
|
+
CSS_PATH = "Db4E.tcss"
|
|
95
|
+
|
|
96
|
+
def __init__(self, config: Config, **kwargs):
|
|
97
|
+
super().__init__(**kwargs)
|
|
98
|
+
self.config = config
|
|
99
|
+
self.depl_mgr = DeploymentMgr(config)
|
|
100
|
+
self.install_mgr = InstallMgr(config)
|
|
101
|
+
self.pane_catalogue = PaneCatalogue()
|
|
102
|
+
self.initialized_flag = True if self.depl_mgr.is_initialized() else False
|
|
103
|
+
self.pane_mgr = PaneMgr(
|
|
104
|
+
config=config, catalogue=self.pane_catalogue, initialized_flag=self.initialized_flag)
|
|
105
|
+
|
|
106
|
+
# Setup the themes
|
|
107
|
+
theme = RICH_THEME
|
|
108
|
+
self.console.push_theme(theme)
|
|
109
|
+
self.console.set_window_title(self.TITLE)
|
|
110
|
+
theme = TEXTUAL_THEME
|
|
111
|
+
self.register_theme(theme)
|
|
112
|
+
self.theme = "custom"
|
|
113
|
+
|
|
114
|
+
def compose(self):
|
|
115
|
+
self.topbar = TopBar(app_version=__version__)
|
|
116
|
+
yield self.topbar
|
|
117
|
+
yield Vertical(
|
|
118
|
+
NavPane(initialized=self.initialized_flag),
|
|
119
|
+
Clock()
|
|
120
|
+
)
|
|
121
|
+
yield self.pane_mgr
|
|
122
|
+
|
|
123
|
+
### Message handling happens here...
|
|
124
|
+
|
|
125
|
+
# Every form sends it's data here, we need to route the messages
|
|
126
|
+
async def on_submit_form_data(self, message: SubmitFormData) -> None:
|
|
127
|
+
target_module = message.form_data['to_module']
|
|
128
|
+
target_method = message.form_data['to_method']
|
|
129
|
+
|
|
130
|
+
if target_module == 'InstallMgr':
|
|
131
|
+
if target_method == 'initial_setup':
|
|
132
|
+
results = await self.install_mgr.initial_setup(message.form_data)
|
|
133
|
+
self.pane_mgr.set_pane(name="InstallResults", data=results)
|
|
134
|
+
|
|
135
|
+
# The individual Detail panes use this to update the TopBar
|
|
136
|
+
async def on_update_top_bar(self, message: UpdateTopBar) -> None:
|
|
137
|
+
self.topbar.set_state(title=message.title, sub_title=message.sub_title )
|
|
138
|
+
|
|
139
|
+
# This is how the Detail panes is selected and loaded, including any data
|
|
140
|
+
async def on_switch_pane(self, message: SwitchPane) -> None:
|
|
141
|
+
self.pane_mgr.set_pane(message.pane_name, message.data)
|
|
142
|
+
|
|
143
|
+
# NavPane selections are routed here
|
|
144
|
+
async def on_nav_leaf_selected(self, message: NavLeafSelected) -> None:
|
|
145
|
+
category = message.parent
|
|
146
|
+
instance = message.leaf
|
|
147
|
+
print(f"Got it: {repr(category)}/{repr(instance)}")
|
|
148
|
+
if category == 'Deployments' and instance == 'Db4E Core':
|
|
149
|
+
db4e_data = self.depl_mgr.get_deployment('db4e')
|
|
150
|
+
print(f"db4e_data: {db4e_data}")
|
|
151
|
+
self.pane_mgr.set_pane(name="Db4E", data=db4e_data)
|
|
152
|
+
|
|
153
|
+
def _handle_exception(self, error: Exception) -> None:
|
|
154
|
+
self.bell()
|
|
155
|
+
self.exit(message=Traceback(show_locals=True, width=None, locals_max_length=5))
|
|
156
|
+
|
|
157
|
+
def main():
|
|
158
|
+
# Set environment variables for better color support
|
|
159
|
+
os.environ["TERM"] = "xterm-256color"
|
|
160
|
+
os.environ["COLORTERM"] = "truecolor"
|
|
161
|
+
|
|
162
|
+
config_manager = ConfigMgr(__version__)
|
|
163
|
+
config = config_manager.get_config()
|
|
164
|
+
app = Db4EApp(config)
|
|
165
|
+
app.run()
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
main()
|
db4e/Db4E.tcss
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Root screen layout */
|
|
2
|
+
Screen {
|
|
3
|
+
layout: grid;
|
|
4
|
+
grid-size: 2;
|
|
5
|
+
grid-columns: 1fr 4fr;
|
|
6
|
+
grid-rows: 13fr 1fr;
|
|
7
|
+
grid-gutter: 0 1;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Clock {
|
|
11
|
+
height: 3;
|
|
12
|
+
padding: 0 2;
|
|
13
|
+
border: round;
|
|
14
|
+
align: center middle;
|
|
15
|
+
background: black;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
DetailPane {
|
|
19
|
+
height: 1fr;
|
|
20
|
+
padding: 0 2;
|
|
21
|
+
border: round;
|
|
22
|
+
row-span: 2;
|
|
23
|
+
background: black;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Db4E,
|
|
27
|
+
Welcome,
|
|
28
|
+
InstallResults {
|
|
29
|
+
padding: 0 2;
|
|
30
|
+
border: round;
|
|
31
|
+
background: black;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
NavPane {
|
|
35
|
+
height: 9fr;
|
|
36
|
+
padding: 0 2;
|
|
37
|
+
border: round;
|
|
38
|
+
background: black;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
TopBar {
|
|
42
|
+
height: 3;
|
|
43
|
+
padding: 0 2;
|
|
44
|
+
border: round;
|
|
45
|
+
dock: top;
|
|
46
|
+
background: black
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#labeled_input_label {
|
|
50
|
+
align: right middle;
|
|
51
|
+
height: 1;
|
|
52
|
+
width: 25;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#topbar_title {
|
|
56
|
+
content-align: center middle;
|
|
57
|
+
width: 100%;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#tree_deployments {
|
|
61
|
+
height: auto;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#tree_metrics {
|
|
65
|
+
height: auto;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#initial_setup_form {
|
|
69
|
+
height: 5;
|
|
70
|
+
border: round;
|
|
71
|
+
}
|
|
72
|
+
#initial_setup_db4e_group_label {
|
|
73
|
+
padding: 0 1;
|
|
74
|
+
width: 23;
|
|
75
|
+
}
|
|
76
|
+
#initial_setup_vendor_dir_label {
|
|
77
|
+
padding: 0 1;
|
|
78
|
+
width: 23;
|
|
79
|
+
}
|
|
80
|
+
#initial_setup_user_wallet_label {
|
|
81
|
+
padding: 0 1;
|
|
82
|
+
width: 23;
|
|
83
|
+
}
|
|
84
|
+
#initial_setup_db4e_group_input {
|
|
85
|
+
width: 10;
|
|
86
|
+
}
|
|
87
|
+
#initial_setup_vendor_dir_input {
|
|
88
|
+
width: 30;
|
|
89
|
+
}
|
|
90
|
+
#initial_setup_user_wallet_input {
|
|
91
|
+
width: 75;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.form_intro {
|
|
95
|
+
border: round;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
Button {
|
|
99
|
+
align: center middle;
|
|
100
|
+
height: 3;
|
|
101
|
+
border: round;
|
|
102
|
+
width: 20;
|
|
103
|
+
}
|
|
104
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Messages/NavLeafSelected.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
|
|
9
|
+
Usage example:
|
|
10
|
+
await self.post_message(NavLeafSelected(self)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from textual.widget import Widget
|
|
14
|
+
from textual.message import Message
|
|
15
|
+
|
|
16
|
+
class NavLeafSelected(Message):
|
|
17
|
+
def __init__(self, sender: Widget, parent: str, leaf: str) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
self.sender = sender
|
|
20
|
+
self.leaf = str(leaf)
|
|
21
|
+
self.parent = str(parent)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Messages/RefreshNavPane.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
|
|
9
|
+
Usage example:
|
|
10
|
+
await self.post_message(RefreshNavPane(self)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from textual.widget import Widget
|
|
14
|
+
from textual.message import Message
|
|
15
|
+
|
|
16
|
+
class RefreshNavPane(Message):
|
|
17
|
+
def __init__(self, sender: Widget) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
self.sender = sender
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Messages/SubmitFormData.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
|
|
9
|
+
Usage example:
|
|
10
|
+
await self.post_message(SubmitFormData(self, resultData))
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from textual.widget import Widget
|
|
14
|
+
from textual.message import Message
|
|
15
|
+
|
|
16
|
+
class SubmitFormData(Message):
|
|
17
|
+
def __init__(self, sender: Widget, form_data: dict) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
self.form_data = form_data
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Messages/SwitchPane.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
|
|
9
|
+
Usage example:
|
|
10
|
+
await self.post_message(SwitchPane(self, "InstallResults", results))
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from textual.widget import Widget
|
|
14
|
+
from textual.message import Message
|
|
15
|
+
|
|
16
|
+
class SwitchPane(Message):
|
|
17
|
+
def __init__(self, sender: Widget, pane_name: str, data: dict = None):
|
|
18
|
+
super().__init__()
|
|
19
|
+
self.pane_name = str(pane_name)
|
|
20
|
+
self.data = data
|
|
21
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Messages/UpdateTopBar.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
|
|
9
|
+
Usage example:
|
|
10
|
+
await self.post_message(UpdateTopBar(self, "Database 4 Everything ", "Welcome"))
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from textual.widget import Widget
|
|
14
|
+
from textual.message import Message
|
|
15
|
+
|
|
16
|
+
class UpdateTopBar(Message):
|
|
17
|
+
def __init__(self, sender: Widget, title: str, sub_title: str) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
self.title = title
|
|
20
|
+
self.sub_title = sub_title
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Modules/ConfigManager.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
import argparse
|
|
12
|
+
|
|
13
|
+
class ConfigMgr:
|
|
14
|
+
def __init__(self, app_version: str):
|
|
15
|
+
parser = argparse.ArgumentParser(description="Db4E command line switches")
|
|
16
|
+
parser.add_argument("-b", "--backup", action="store_true", help="Perform a db4e backup.")
|
|
17
|
+
parser.add_argument("-s", "--service", action="store_true", help="Run db4e as a service.")
|
|
18
|
+
parser.add_argument("-v", "--version", action="store_true", help="Print the db4e version.")
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
|
|
21
|
+
ini = Config(app_version=app_version)
|
|
22
|
+
if args.backup:
|
|
23
|
+
ini.config['db4e']['op'] = 'run_backup'
|
|
24
|
+
elif args.service:
|
|
25
|
+
ini.config['db4e']['op'] = 'run_daemon'
|
|
26
|
+
elif args.version:
|
|
27
|
+
print(f'Db4e v{app_version}')
|
|
28
|
+
sys.exit(0)
|
|
29
|
+
else:
|
|
30
|
+
ini.config['db4e']['op'] = 'run_ui'
|
|
31
|
+
self.ini = ini
|
|
32
|
+
|
|
33
|
+
def get_config(self):
|
|
34
|
+
return self.ini
|
|
35
|
+
|
|
36
|
+
class Config:
|
|
37
|
+
def __init__(self, app_version: str):
|
|
38
|
+
self.config = {
|
|
39
|
+
'db4e': {
|
|
40
|
+
'app_version': app_version,
|
|
41
|
+
'op': 'run_ui',
|
|
42
|
+
'api_dir': 'api',
|
|
43
|
+
'bin_dir': 'bin',
|
|
44
|
+
'conf_dir': 'conf',
|
|
45
|
+
'db4e_dir': 'db4e',
|
|
46
|
+
'desc': 'Database 4 Everything',
|
|
47
|
+
'dev_dir': 'dev',
|
|
48
|
+
'log_dir': 'logs',
|
|
49
|
+
'process': 'db4e.sh',
|
|
50
|
+
'pypi_repository': 'https://pypi.org/pypi/db4e/json',
|
|
51
|
+
'refresh_interval': 15,
|
|
52
|
+
'run_dir': 'run',
|
|
53
|
+
'service_file': 'db4e.service',
|
|
54
|
+
'setup_script': 'db4e-initial-setup.sh',
|
|
55
|
+
'service_install_script': 'db4e-install-service.sh',
|
|
56
|
+
'service_log_file': 'db4e.log',
|
|
57
|
+
'service_uninstaller': 'db4e-uninstall-service.sh',
|
|
58
|
+
'src_dir': 'src',
|
|
59
|
+
'systemd_dir': 'systemd',
|
|
60
|
+
'template_dir': 'Templates',
|
|
61
|
+
'vendor_dir': 'vendor',
|
|
62
|
+
},
|
|
63
|
+
'db': {
|
|
64
|
+
'backup_dir': 'backups',
|
|
65
|
+
'backup_script':'db4e-backup.sh',
|
|
66
|
+
'collection': 'mining',
|
|
67
|
+
'depl_collection': 'depl',
|
|
68
|
+
'log_collection': 'logging',
|
|
69
|
+
# How many days of data to keep in the logging collection
|
|
70
|
+
'log_retention_days': 7,
|
|
71
|
+
'max_backups': 7,
|
|
72
|
+
'metrics_collection': 'metrics',
|
|
73
|
+
'name': 'db4e',
|
|
74
|
+
'port': 27017,
|
|
75
|
+
'retry_timeout': 15,
|
|
76
|
+
'server': 'localhost',
|
|
77
|
+
},
|
|
78
|
+
'monerod': {
|
|
79
|
+
'blockchain_dir': 'monero-blockchain',
|
|
80
|
+
'config': 'monerod.ini',
|
|
81
|
+
'desc': 'Monero Blockchain Daemon',
|
|
82
|
+
'log_file': 'monerod.log',
|
|
83
|
+
'process': 'monerod',
|
|
84
|
+
'service_file': 'monerod@.service',
|
|
85
|
+
'socket_file': 'monerod@.socket',
|
|
86
|
+
'stdin_pipe': 'monerod.stdin',
|
|
87
|
+
'start_script': 'start-monerod.sh',
|
|
88
|
+
'version': '0.18.4.0',
|
|
89
|
+
},
|
|
90
|
+
'p2pool': {
|
|
91
|
+
'config': 'p2pool.ini',
|
|
92
|
+
'desc': 'P2Pool Daemon',
|
|
93
|
+
'log_file': 'p2pool.log',
|
|
94
|
+
'process': 'p2pool',
|
|
95
|
+
'service_file': 'p2pool@.service',
|
|
96
|
+
'socket_file': 'p2pool@.socket',
|
|
97
|
+
'start_script': 'start-p2pool.sh',
|
|
98
|
+
'stdin': 'p2pool.stdin',
|
|
99
|
+
'version': '4.8',
|
|
100
|
+
},
|
|
101
|
+
'xmrig': {
|
|
102
|
+
'desc': 'XMRig Miner',
|
|
103
|
+
'conf_dir': 'conf',
|
|
104
|
+
'config': 'config.json',
|
|
105
|
+
'permissions': '-rwsr-x---',
|
|
106
|
+
'process': 'xmrig',
|
|
107
|
+
'service_file': 'xmrig@.service',
|
|
108
|
+
'version': '6.23.0',
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
db4e/Modules/DbMgr.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Modules/DbManager.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import time
|
|
11
|
+
from pymongo import MongoClient
|
|
12
|
+
from pymongo.errors import ConnectionFailure, CollectionInvalid
|
|
13
|
+
|
|
14
|
+
from db4e.Modules.ConfigMgr import Config
|
|
15
|
+
from db4e.Templates.db.Deployment import DB4E_RECORD
|
|
16
|
+
|
|
17
|
+
class DbMgr:
|
|
18
|
+
|
|
19
|
+
def __init__(self, config: Config):
|
|
20
|
+
self.ini = config
|
|
21
|
+
# MongoDB settings
|
|
22
|
+
retry_timeout = self.ini.config['db']['retry_timeout']
|
|
23
|
+
db_server = self.ini.config['db']['server']
|
|
24
|
+
db_port = self.ini.config['db']['port']
|
|
25
|
+
self.max_backups = self.ini.config['db']['max_backups']
|
|
26
|
+
self.db_name = self.ini.config['db']['name']
|
|
27
|
+
self.db_collection = self.ini.config['db']['collection']
|
|
28
|
+
self.depl_collection = self.ini.config['db']['depl_collection']
|
|
29
|
+
self.log_collection = self.ini.config['db']['log_collection']
|
|
30
|
+
self.log_retention = self.ini.config['db']['log_retention_days']
|
|
31
|
+
self.metrics_collection = self.ini.config['db']['metrics_collection']
|
|
32
|
+
# TODO Setup logging
|
|
33
|
+
#self.log = Db4eLogger('Db4eDb')
|
|
34
|
+
|
|
35
|
+
# Connect to MongoDB
|
|
36
|
+
db_uri = f'mongodb://{db_server}:{db_port}'
|
|
37
|
+
try:
|
|
38
|
+
self._client = MongoClient(db_uri)
|
|
39
|
+
except ConnectionFailure as e:
|
|
40
|
+
# TODO factor in the old Db4eLogger code....
|
|
41
|
+
#self.log.critical(f'Connection failed: {e}. Retrying in {retry_timeout} seconds...')
|
|
42
|
+
time.sleep(retry_timeout)
|
|
43
|
+
|
|
44
|
+
self.db4e = self._client[self.db_name]
|
|
45
|
+
|
|
46
|
+
# Used for backups
|
|
47
|
+
self.db4e_dir = None
|
|
48
|
+
self.repo_dir = None
|
|
49
|
+
self.init_db()
|
|
50
|
+
|
|
51
|
+
def ensure_indexes(self):
|
|
52
|
+
log_col = self.get_collection(self.log_collection)
|
|
53
|
+
if "timestamp_1" not in log_col.index_information():
|
|
54
|
+
log_col.create_index("timestamp")
|
|
55
|
+
# TODO self.log.debug("Created index on 'timestamp' for log collection.")
|
|
56
|
+
|
|
57
|
+
def find_one(self, col_name, filter):
|
|
58
|
+
col = self.get_collection(col_name)
|
|
59
|
+
return col.find_one(filter)
|
|
60
|
+
|
|
61
|
+
def get_collection(self, col_name):
|
|
62
|
+
return self.db4e[col_name]
|
|
63
|
+
|
|
64
|
+
def get_new_rec(self, rec_type):
|
|
65
|
+
if rec_type == 'db4e':
|
|
66
|
+
return DB4E_RECORD
|
|
67
|
+
|
|
68
|
+
def init_db(self):
|
|
69
|
+
# Make sure the 'db4e' database, core collections and indexes exist.
|
|
70
|
+
db_col = self.db_collection
|
|
71
|
+
log_col = self.log_collection
|
|
72
|
+
depl_col = self.depl_collection
|
|
73
|
+
metrics_col = self.metrics_collection
|
|
74
|
+
db_col_names = self.db4e.list_collection_names()
|
|
75
|
+
for aCol in [ db_col, log_col, depl_col, metrics_col ]:
|
|
76
|
+
if aCol not in db_col_names:
|
|
77
|
+
try:
|
|
78
|
+
self.db4e.create_collection(aCol)
|
|
79
|
+
if aCol == log_col:
|
|
80
|
+
log_col = self.get_collection(log_col)
|
|
81
|
+
log_col.create_index('timestamp')
|
|
82
|
+
except CollectionInvalid:
|
|
83
|
+
# TODO self.log.warning(f"Attempted to create existing collection: {aCol}")
|
|
84
|
+
pass
|
|
85
|
+
# TODO self.log.debug(f'Created DB collection ({aCol})')
|
|
86
|
+
self.ensure_indexes()
|
|
87
|
+
|
|
88
|
+
def insert_one(self, col_name, jdoc):
|
|
89
|
+
collection = self.get_collection(col_name)
|
|
90
|
+
return collection.insert_one(jdoc)
|
|
91
|
+
|
|
92
|
+
def update_one(self, col_name, filter, new_values):
|
|
93
|
+
collection = self.get_collection(col_name)
|
|
94
|
+
return collection.update_one(filter, {'$set' : new_values})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Modules/DeploymentManager.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os, shutil
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
import getpass
|
|
13
|
+
|
|
14
|
+
from db4e.Modules.ConfigMgr import Config
|
|
15
|
+
from db4e.Modules.DbMgr import DbMgr
|
|
16
|
+
|
|
17
|
+
# The Mongo collection that houses the deployment records
|
|
18
|
+
DEPL_COL = 'depl'
|
|
19
|
+
|
|
20
|
+
class DeploymentMgr:
|
|
21
|
+
|
|
22
|
+
def __init__(self, config: Config):
|
|
23
|
+
self.ini = config
|
|
24
|
+
self.db = DbMgr(config)
|
|
25
|
+
self.col_name = DEPL_COL
|
|
26
|
+
|
|
27
|
+
def add_deployment(self, rec):
|
|
28
|
+
rec['doc_type'] = 'deployment'
|
|
29
|
+
rec['updated'] = datetime.now(timezone.utc)
|
|
30
|
+
# Get the component version from the static YAML config file
|
|
31
|
+
if rec['component'] == 'db4e':
|
|
32
|
+
rec['user'] = getpass.getuser()
|
|
33
|
+
else:
|
|
34
|
+
rec['version'] = self.ini.config[rec['component']]['version']
|
|
35
|
+
self.db.insert_one(self.col_name, rec)
|
|
36
|
+
|
|
37
|
+
def is_initialized(self):
|
|
38
|
+
rec = self.db.find_one(self.col_name, {'doc_type': 'deployment', 'component': 'db4e'})
|
|
39
|
+
if rec:
|
|
40
|
+
return True
|
|
41
|
+
else:
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
def get_deployment(self, component):
|
|
45
|
+
print(f"DeploymentMgr:get_deployment(): {component}")
|
|
46
|
+
# Ask the db for the component record
|
|
47
|
+
db_rec = self.db.find_one(self.col_name, {'doc_type': 'deployment', 'component': component})
|
|
48
|
+
# rec is a cursor object.
|
|
49
|
+
if db_rec:
|
|
50
|
+
rec = {}
|
|
51
|
+
component = db_rec['component']
|
|
52
|
+
if component == 'db4e':
|
|
53
|
+
rec['group'] = db_rec['group']
|
|
54
|
+
rec['install_dir'] = db_rec['install_dir']
|
|
55
|
+
rec['user'] = db_rec['user']
|
|
56
|
+
rec['user_wallet'] = db_rec['user_wallet']
|
|
57
|
+
rec['vendor_dir'] = db_rec['vendor_dir']
|
|
58
|
+
return rec
|
|
59
|
+
# No record for this deployment exists
|
|
60
|
+
|
|
61
|
+
# Check if this is the first time the app has been run
|
|
62
|
+
rec = self.db.find_one(self.col_name, {'doc_type': 'deployment', 'component': 'db4e'})
|
|
63
|
+
if not rec:
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
def get_deployment_by_instance(self, component, instance):
|
|
67
|
+
if instance == 'db4e core':
|
|
68
|
+
return self.get_deployment('db4e')
|
|
69
|
+
|
|
70
|
+
def update_deployent(self, rec):
|
|
71
|
+
component = rec['component']
|
|
72
|
+
if component == 'db4e':
|
|
73
|
+
filter = {'doc_type': 'deployment', 'component': 'db4e'}
|
|
74
|
+
self.db.update_one(self.col_name, filter, rec)
|