simo 2.1.0__py3-none-any.whl → 2.1.3__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.
Potentially problematic release.
This version of simo might be problematic. Click here for more details.
- simo/__pycache__/asgi.cpython-38.pyc +0 -0
- simo/asgi.py +1 -1
- simo/core/__pycache__/admin.cpython-38.pyc +0 -0
- simo/core/__pycache__/api.cpython-38.pyc +0 -0
- simo/core/__pycache__/apps.cpython-38.pyc +0 -0
- simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/core/__pycache__/dynamic_settings.cpython-38.pyc +0 -0
- simo/core/__pycache__/forms.cpython-38.pyc +0 -0
- simo/core/__pycache__/models.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/__pycache__/views.cpython-38.pyc +0 -0
- simo/core/admin.py +0 -18
- simo/core/api.py +1 -0
- simo/core/apps.py +4 -1
- simo/core/auto_urls.py +2 -3
- simo/core/dynamic_settings.py +0 -8
- simo/core/forms.py +1 -98
- simo/core/migrations/0038_remove_instance_cover_image_and_more.py +30 -0
- simo/core/migrations/__pycache__/0038_remove_instance_cover_image_and_more.cpython-38.pyc +0 -0
- simo/core/models.py +2 -7
- simo/core/tasks.py +95 -51
- simo/core/templates/admin/user_tools.html +0 -3
- simo/core/views.py +2 -85
- simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/fleet/controllers.py +1 -1
- simo/fleet/migrations/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.py +27 -0
- simo/fleet/migrations/__pycache__/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.cpython-38.pyc +0 -0
- simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
- simo/generic/controllers.py +2 -2
- simo/generic/forms.py +0 -3
- simo/generic/templates/admin/controller_widgets/weather_forecast.html +1 -1
- simo/management/__init__.py +0 -0
- simo/management/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/management/__pycache__/on_http_start.cpython-38.pyc +0 -0
- simo/{_hub_template → management/_hub_template}/hub/nginx.conf +2 -2
- simo/{auto_update.py → management/auto_update.py} +3 -0
- simo/{cli.py → management/copy_template.py} +3 -16
- simo/management/install.py +258 -0
- simo/{on_http_start.py → management/on_http_start.py} +22 -2
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/METADATA +1 -1
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/RECORD +52 -44
- simo-2.1.3.dist-info/entry_points.txt +2 -0
- simo/__pycache__/on_http_start.cpython-38.pyc +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/asgi.py +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/celeryc.py +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/manage.py +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/settings.py +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/supervisor.conf +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/urls.py +0 -0
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/LICENSE.md +0 -0
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/WHEEL +0 -0
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/top_level.txt +0 -0
|
@@ -16,9 +16,6 @@
|
|
|
16
16
|
<i class="fas fa-sort-down"></i>
|
|
17
17
|
</div>
|
|
18
18
|
<div class="dropdown-content" style="right: 110px">
|
|
19
|
-
<a href="{% url 'setup-wizard' %}">
|
|
20
|
-
<i class="fas fa-magic"></i> Core Setup Wizard
|
|
21
|
-
</a>
|
|
22
19
|
<a href="{% url 'update' %}" title="Update hub" class="update_link">
|
|
23
20
|
<i class="fas fa-angle-double-up"></i> Update
|
|
24
21
|
</a>
|
simo/core/views.py
CHANGED
|
@@ -3,100 +3,17 @@ import threading
|
|
|
3
3
|
import subprocess
|
|
4
4
|
from django.contrib.auth.decorators import login_required
|
|
5
5
|
from django.urls import reverse
|
|
6
|
-
from django.shortcuts import
|
|
7
|
-
from django.http import
|
|
8
|
-
from django.contrib.gis.geos import Point
|
|
6
|
+
from django.shortcuts import redirect
|
|
7
|
+
from django.http import HttpResponse, Http404
|
|
9
8
|
from django.contrib import messages
|
|
10
|
-
from simo.users.models import User
|
|
11
|
-
from simo.conf import dynamic_settings
|
|
12
9
|
from .models import Instance
|
|
13
10
|
from .tasks import update as update_task, supervisor_restart
|
|
14
|
-
from .forms import (
|
|
15
|
-
HubConfigForm, CoordinatesForm, TermsAndConditionsForm
|
|
16
|
-
)
|
|
17
11
|
from .middleware import introduce_instance
|
|
18
12
|
|
|
19
13
|
|
|
20
14
|
def get_timestamp(request):
|
|
21
15
|
return HttpResponse(time.time())
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
@login_required
|
|
25
|
-
def setup_wizard(request):
|
|
26
|
-
|
|
27
|
-
step = request.session.get('setup_wizard_step', 1)
|
|
28
|
-
|
|
29
|
-
if request.method == 'POST' and 'back' in request.POST and step > 1:
|
|
30
|
-
request.session['setup_wizard_step'] = step - 1
|
|
31
|
-
return redirect(request.path)
|
|
32
|
-
|
|
33
|
-
if step == 1:
|
|
34
|
-
cover_img = dynamic_settings['core__cover_image']
|
|
35
|
-
if cover_img:
|
|
36
|
-
cover_img.field.storage = cover_img.storage
|
|
37
|
-
initial = {
|
|
38
|
-
'name': dynamic_settings['core__hub_name'],
|
|
39
|
-
'uid': dynamic_settings['core__hub_uid'],
|
|
40
|
-
'time_zone': dynamic_settings['core__time_zone'],
|
|
41
|
-
'units_of_measure': dynamic_settings['core__units_of_measure'],
|
|
42
|
-
'cover_image': cover_img
|
|
43
|
-
}
|
|
44
|
-
form = HubConfigForm(initial=initial, user=request.user)
|
|
45
|
-
if request.method == 'POST':
|
|
46
|
-
form = HubConfigForm(
|
|
47
|
-
request.POST, request.FILES, initial=initial, user=request.user
|
|
48
|
-
)
|
|
49
|
-
if form.is_valid():
|
|
50
|
-
dynamic_settings['core__hub_name'] = form.cleaned_data['name']
|
|
51
|
-
dynamic_settings['core__hub_uid'] = form.cleaned_data['uid']
|
|
52
|
-
dynamic_settings['core__time_zone'] = form.cleaned_data[
|
|
53
|
-
'time_zone'
|
|
54
|
-
]
|
|
55
|
-
dynamic_settings['core__units_of_measure'] = form.cleaned_data[
|
|
56
|
-
'units_of_measure'
|
|
57
|
-
]
|
|
58
|
-
if not dynamic_settings['core__location_coordinates'] \
|
|
59
|
-
and request.POST.get('location-guess'):
|
|
60
|
-
dynamic_settings['core__location_coordinates'] = \
|
|
61
|
-
request.POST['location-guess']
|
|
62
|
-
if form.cleaned_data['cover_image']:
|
|
63
|
-
dynamic_settings['core__cover_image'] = \
|
|
64
|
-
form.cleaned_data['cover_image']
|
|
65
|
-
dynamic_settings['core__cover_image_synced'] = False
|
|
66
|
-
request.session['setup_wizard_step'] = 2
|
|
67
|
-
return redirect(request.path)
|
|
68
|
-
elif step == 2:
|
|
69
|
-
initial = {
|
|
70
|
-
'location': dynamic_settings['core__location_coordinates'],
|
|
71
|
-
'share_location': dynamic_settings['core__share_location']
|
|
72
|
-
}
|
|
73
|
-
form = CoordinatesForm(initial=initial)
|
|
74
|
-
if request.method == 'POST':
|
|
75
|
-
form = CoordinatesForm(request.POST, initial=initial)
|
|
76
|
-
if form.is_valid():
|
|
77
|
-
dynamic_settings['core__location_coordinates'] = \
|
|
78
|
-
form.cleaned_data['location']
|
|
79
|
-
dynamic_settings['core__share_location'] = \
|
|
80
|
-
form.cleaned_data['share_location']
|
|
81
|
-
request.session['setup_wizard_step'] = 3
|
|
82
|
-
return redirect(request.path)
|
|
83
|
-
else:
|
|
84
|
-
form = TermsAndConditionsForm()
|
|
85
|
-
if request.method == 'POST':
|
|
86
|
-
form = TermsAndConditionsForm(request.POST)
|
|
87
|
-
if form.is_valid() and form.cleaned_data['accept']:
|
|
88
|
-
request.session.pop('setup_wizard_step')
|
|
89
|
-
messages.success(
|
|
90
|
-
request, "Congratulations! "
|
|
91
|
-
"Your Hub is now configured and restarting in the background. "
|
|
92
|
-
"Will be fully ready in 30 seconds."
|
|
93
|
-
)
|
|
94
|
-
threading.Thread(target=supervisor_restart).start()
|
|
95
|
-
return redirect(reverse('admin:index'))
|
|
96
|
-
|
|
97
|
-
return render(request, 'setup_wizard/form.html', {'form': form, 'step': step})
|
|
98
|
-
|
|
99
|
-
|
|
100
17
|
@login_required
|
|
101
18
|
def update(request):
|
|
102
19
|
if not request.user.is_superuser:
|
|
Binary file
|
simo/fleet/controllers.py
CHANGED
|
@@ -158,7 +158,7 @@ class BaseClimateSensor(FleeDeviceMixin, BasicSensorMixin, BaseMultiSensor):
|
|
|
158
158
|
def __init__(self, *args, **kwargs):
|
|
159
159
|
super().__init__(*args, **kwargs)
|
|
160
160
|
self.sys_temp_units = 'C'
|
|
161
|
-
if
|
|
161
|
+
if self.component.zone.instance.units_of_measure == 'imperial':
|
|
162
162
|
self.sys_temp_units = 'F'
|
|
163
163
|
|
|
164
164
|
@property
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2024-06-12 08:14
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('fleet', '0036_auto_20240605_0702'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterModelOptions(
|
|
14
|
+
name='colonelpin',
|
|
15
|
+
options={'ordering': ('colonel', 'no')},
|
|
16
|
+
),
|
|
17
|
+
migrations.AlterField(
|
|
18
|
+
model_name='colonelpin',
|
|
19
|
+
name='no',
|
|
20
|
+
field=models.PositiveIntegerField(db_index=True),
|
|
21
|
+
),
|
|
22
|
+
migrations.AlterField(
|
|
23
|
+
model_name='interfaceaddress',
|
|
24
|
+
name='address_type',
|
|
25
|
+
field=models.CharField(choices=[('i2c', 'I2C'), ('dali-gear', 'DALI Gear'), ('dali-group', 'DALI Gear Group'), ('dali-device', 'DALI Control Device')], db_index=True, max_length=100),
|
|
26
|
+
),
|
|
27
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/generic/controllers.py
CHANGED
|
@@ -223,7 +223,7 @@ class Thermostat(ControllerBase):
|
|
|
223
223
|
def default_config(self):
|
|
224
224
|
min = 3
|
|
225
225
|
max = 36
|
|
226
|
-
if
|
|
226
|
+
if self.component.zone.instance.units_of_measure == 'imperial':
|
|
227
227
|
min = 36
|
|
228
228
|
max = 100
|
|
229
229
|
return {
|
|
@@ -237,7 +237,7 @@ class Thermostat(ControllerBase):
|
|
|
237
237
|
raise ValidationError("This component type does not accept set value!")
|
|
238
238
|
|
|
239
239
|
def _get_default_user_config(self):
|
|
240
|
-
if
|
|
240
|
+
if self.component.zone.instance.units_of_measure == 'imperial':
|
|
241
241
|
target_temp = 70
|
|
242
242
|
low_target = 60
|
|
243
243
|
high_target = 75
|
simo/generic/forms.py
CHANGED
|
@@ -273,9 +273,6 @@ class ThermostatConfigForm(BaseComponentForm):
|
|
|
273
273
|
|
|
274
274
|
def __init__(self, *args, **kwargs):
|
|
275
275
|
super().__init__(*args, **kwargs)
|
|
276
|
-
if dynamic_settings['core__units_of_measure'] == 'imperial':
|
|
277
|
-
self.fields['min'].initial = 36
|
|
278
|
-
self.fields['max'].initial = 100
|
|
279
276
|
if self.instance.pk:
|
|
280
277
|
self.fields['mode'].initial = \
|
|
281
278
|
self.instance.config['user_config']['mode']
|
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
position: relative;
|
|
10
10
|
bottom: -4px;
|
|
11
11
|
"></span>
|
|
12
|
-
{{ obj.value.current.temp }}ᴼ {% if
|
|
12
|
+
{{ obj.value.current.temp }}ᴼ {% if obj__zone__instance__units_of_measure == 'metric' %}C{% else %}F{% endif %}
|
|
13
13
|
</div>
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
@@ -4,7 +4,7 @@ server{
|
|
|
4
4
|
|
|
5
5
|
charset utf-8;
|
|
6
6
|
|
|
7
|
-
client_max_body_size
|
|
7
|
+
client_max_body_size 100M;
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
location /protected/static {
|
|
@@ -55,7 +55,7 @@ server{
|
|
|
55
55
|
|
|
56
56
|
ssl_session_cache shared:TLS:2m;
|
|
57
57
|
|
|
58
|
-
client_max_body_size
|
|
58
|
+
client_max_body_size 100M;
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
location /protected/static {
|
|
@@ -26,6 +26,9 @@ def perform_update():
|
|
|
26
26
|
if proc.returncode:
|
|
27
27
|
raise Exception(err.decode())
|
|
28
28
|
|
|
29
|
+
from simo.management.install import install_dependencies
|
|
30
|
+
install_dependencies()
|
|
31
|
+
|
|
29
32
|
proc = subprocess.Popen(
|
|
30
33
|
[os.path.join(HUB_DIR, 'manage.py'), 'migrate'],
|
|
31
34
|
cwd=HUB_DIR,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env python
|
|
2
2
|
import sys
|
|
3
|
-
import click
|
|
4
3
|
import os
|
|
5
4
|
import shutil
|
|
6
5
|
import simo
|
|
@@ -8,7 +7,7 @@ from django.template import Context, Engine
|
|
|
8
7
|
from django.core.management.utils import get_random_secret_key
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
def
|
|
10
|
+
def copy_template(to_directory='/etc/SIMO'):
|
|
12
11
|
template_file_extensions = ['.py', '.conf']
|
|
13
12
|
|
|
14
13
|
context = Context({
|
|
@@ -49,18 +48,6 @@ def copy_default_template(to_directory='/etc/SIMO'):
|
|
|
49
48
|
os.makedirs(os.path.join(root, dirname), exist_ok=True)
|
|
50
49
|
|
|
51
50
|
|
|
52
|
-
@click.command()
|
|
53
|
-
def init():
|
|
54
|
-
copy_default_template()
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@click.group()
|
|
58
|
-
def main(args=None):
|
|
59
|
-
pass
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
main.add_command(init)
|
|
63
|
-
|
|
64
51
|
|
|
65
52
|
if __name__ == "__main__":
|
|
66
|
-
sys.exit(
|
|
53
|
+
sys.exit(copy_template())
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
This should be called after simo has just been installed.
|
|
5
|
+
It prepares and powers up SIMO.io hub.
|
|
6
|
+
However it is not guaranteed to be fully operational.
|
|
7
|
+
"""
|
|
8
|
+
import os, sys, json, subprocess, socket, shutil, traceback
|
|
9
|
+
import simo
|
|
10
|
+
from django.template import Context, Engine
|
|
11
|
+
from django.core.management.utils import get_random_secret_key
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def install_dependencies():
|
|
16
|
+
|
|
17
|
+
status = subprocess.call(
|
|
18
|
+
'apt install postgresql libpq-dev postgresql-client '
|
|
19
|
+
'postgresql-client-common python3-pip redis-server supervisor '
|
|
20
|
+
'mosquitto libopenjp2-7 libtiff5 pkg-config libcairo2-dev '
|
|
21
|
+
'libgirepository1.0-dev libcairo2 libudev-dev gdal-bin net-tools '
|
|
22
|
+
'timeshift nginx postgis openvpn ffmpeg libsm6 libxext6 ssh keychain -y',
|
|
23
|
+
shell=True
|
|
24
|
+
)
|
|
25
|
+
if status != 0:
|
|
26
|
+
print("Unable install required packages.")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
return True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_ip():
|
|
33
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
34
|
+
try:
|
|
35
|
+
# doesn't even have to be reachable
|
|
36
|
+
s.connect(('10.255.255.255', 1))
|
|
37
|
+
IP = s.getsockname()[0]
|
|
38
|
+
except Exception:
|
|
39
|
+
IP = '127.0.0.1'
|
|
40
|
+
finally:
|
|
41
|
+
s.close()
|
|
42
|
+
return IP
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def copy_template(to_directory='/etc/SIMO'):
|
|
46
|
+
template_file_extensions = ['.py', '.conf']
|
|
47
|
+
|
|
48
|
+
context = Context({
|
|
49
|
+
'secret_key': get_random_secret_key(),
|
|
50
|
+
'project_dir': to_directory,
|
|
51
|
+
'base_dir': to_directory
|
|
52
|
+
}, autoescape=False)
|
|
53
|
+
template_dir = os.path.join(
|
|
54
|
+
os.path.dirname(simo.__file__), '_hub_template'
|
|
55
|
+
)
|
|
56
|
+
prefix_length = len(template_dir) + 1
|
|
57
|
+
for root, dirs, files in os.walk(template_dir):
|
|
58
|
+
relative_dir = root[prefix_length:]
|
|
59
|
+
target_dir = os.path.join(to_directory, relative_dir)
|
|
60
|
+
os.makedirs(target_dir, exist_ok=True)
|
|
61
|
+
for filename in files:
|
|
62
|
+
if filename.endswith(('.pyo', '.pyc', '.py.class')):
|
|
63
|
+
# Ignore some files as they cause various breakages.
|
|
64
|
+
continue
|
|
65
|
+
old_path = os.path.join(root, filename)
|
|
66
|
+
new_path = os.path.join(target_dir, filename)
|
|
67
|
+
os.makedirs(target_dir, exist_ok=True)
|
|
68
|
+
fn, file_extension = os.path.splitext(new_path)
|
|
69
|
+
if file_extension in template_file_extensions:
|
|
70
|
+
with open(old_path, encoding='utf-8') as template_file:
|
|
71
|
+
content = template_file.read()
|
|
72
|
+
template = Engine().from_string(content)
|
|
73
|
+
content = template.render(context)
|
|
74
|
+
with open(new_path, 'w', encoding='utf-8') as new_file:
|
|
75
|
+
new_file.write(content)
|
|
76
|
+
else:
|
|
77
|
+
shutil.copyfile(old_path, new_path)
|
|
78
|
+
shutil.copymode(old_path, new_path)
|
|
79
|
+
for dirname in dirs[:]:
|
|
80
|
+
if dirname.startswith('.') or dirname == '__pycache__':
|
|
81
|
+
dirs.remove(dirname)
|
|
82
|
+
else:
|
|
83
|
+
os.makedirs(os.path.join(root, dirname), exist_ok=True)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def install():
|
|
87
|
+
simo_directory = '/etc/SIMO'
|
|
88
|
+
installed_flag_file_path = os.path.join(simo_directory, 'is_installed.json')
|
|
89
|
+
|
|
90
|
+
if os.path.exists(installed_flag_file_path):
|
|
91
|
+
print("SIMO.io hub is already installed. ")
|
|
92
|
+
print(f"Please delete {installed_flag_file_path} file manually if you want to force this.")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
step = 1
|
|
96
|
+
print(f"{step}.___________Install dependencies__________________")
|
|
97
|
+
status = subprocess.call(
|
|
98
|
+
'apt-add-repository ppa:mosquitto-dev/mosquitto-ppa -y', shell=True
|
|
99
|
+
)
|
|
100
|
+
if status != 0:
|
|
101
|
+
print("Unable to add mosquitto-dev dependency")
|
|
102
|
+
print("Installation failed!")
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
status = subprocess.call('apt-get update -y', shell=True)
|
|
106
|
+
if status != 0:
|
|
107
|
+
print("Unable to apt-update")
|
|
108
|
+
print("Installation failed!")
|
|
109
|
+
return
|
|
110
|
+
success = install_dependencies()
|
|
111
|
+
if not success:
|
|
112
|
+
print("Installation failed!")
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
step += 1
|
|
116
|
+
print(f"{step}.___________Copy default template__________________")
|
|
117
|
+
|
|
118
|
+
if os.path.exists(simo_directory):
|
|
119
|
+
print("Already exists!")
|
|
120
|
+
else:
|
|
121
|
+
try:
|
|
122
|
+
copy_template(simo_directory)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
print(traceback.format_exc(), file=sys.stderr)
|
|
125
|
+
shutil.rmtree(simo_directory, ignore_errors=True)
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
step += 1
|
|
129
|
+
print(f"{step}.___________Configure supervisor__________________")
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
os.remove('/etc/supervisor/conf.d/SIMO.conf', ignore_errors=True)
|
|
133
|
+
except:
|
|
134
|
+
pass
|
|
135
|
+
os.symlink(
|
|
136
|
+
f'{simo_directory}/hub/supervisor.conf',
|
|
137
|
+
'/etc/supervisor/conf.d/SIMO.conf'
|
|
138
|
+
)
|
|
139
|
+
status = subprocess.call(['supervisorctl', 'update', 'all'])
|
|
140
|
+
if status != 0:
|
|
141
|
+
sys.exit("INSTALLATION FAILED! Unable to start supervisord")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
step += 1
|
|
146
|
+
print("%d._____________ Configure NGINX _________________________" % step)
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
os.remove('/etc/nginx/sites-enabled/default')
|
|
150
|
+
except:
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
os.symlink(
|
|
154
|
+
'/etc/SIMO/hub/nginx.conf', '/etc/nginx/sites-enabled/SIMO'
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
os.remove('/etc/ssl/private/localhost.key')
|
|
159
|
+
except:
|
|
160
|
+
pass
|
|
161
|
+
try:
|
|
162
|
+
os.remove('/etc/ssl/certs/localhost.crt')
|
|
163
|
+
except:
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
status = subprocess.call([
|
|
167
|
+
'openssl', 'req', '-x509', '-nodes', '-days', '36500',
|
|
168
|
+
'-newkey', 'rsa:2048',
|
|
169
|
+
'-subj', '/C=US/ST=Denial/L=Springfield/O=Dis/CN=simo.io',
|
|
170
|
+
'-keyout', '/etc/ssl/private/localhost.key',
|
|
171
|
+
'-out', '/etc/ssl/certs/localhost.crt'
|
|
172
|
+
])
|
|
173
|
+
if status != 0:
|
|
174
|
+
sys.exit(
|
|
175
|
+
"INSTALLATION FAILED! Unable to prepare self signed certificate.")
|
|
176
|
+
|
|
177
|
+
status = subprocess.call(['service', 'nginx', 'reload'])
|
|
178
|
+
if status != 0:
|
|
179
|
+
sys.exit("INSTALLATION FAILED! Something is wrong witn NGINX conf.")
|
|
180
|
+
|
|
181
|
+
step += 1
|
|
182
|
+
print("%d._____________ Configure SSH and Firewall_____________" % step)
|
|
183
|
+
new_ssh_conf = ''
|
|
184
|
+
with open('/etc/ssh/sshd_config', 'r') as ssh_conf:
|
|
185
|
+
line = ssh_conf.readline()
|
|
186
|
+
while line:
|
|
187
|
+
if 'PasswordAuthentication' in line:
|
|
188
|
+
new_ssh_conf += 'PasswordAuthentication no'
|
|
189
|
+
else:
|
|
190
|
+
new_ssh_conf += line
|
|
191
|
+
line = ssh_conf.readline()
|
|
192
|
+
with open('/etc/ssh/sshd_config', 'w') as ssh_conf:
|
|
193
|
+
ssh_conf.write(new_ssh_conf)
|
|
194
|
+
|
|
195
|
+
status = subprocess.call(['service', 'ssh', 'restart'])
|
|
196
|
+
if status != 0:
|
|
197
|
+
sys.exit("INSTALLATION FAILED! Unable to restart SSH")
|
|
198
|
+
|
|
199
|
+
stats = []
|
|
200
|
+
stats.append(subprocess.call(['ufw', 'allow', 'ssh']))
|
|
201
|
+
stats.append(subprocess.call(['ufw', 'allow', 'http']))
|
|
202
|
+
stats.append(subprocess.call(['ufw', 'allow', 'https']))
|
|
203
|
+
stats.append(subprocess.call(['ufw', 'allow', '1194']))
|
|
204
|
+
stats.append(subprocess.call(['ufw', 'allow', '1883']))
|
|
205
|
+
if any(stats):
|
|
206
|
+
sys.exit("INSTALLATION FAILED! Unable to update UFW rules")
|
|
207
|
+
|
|
208
|
+
status = subprocess.call('echo y | ufw enable', shell=True)
|
|
209
|
+
if status != 0:
|
|
210
|
+
sys.exit("INSTALLATION FAILED! Unable to enable UFW")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
step += 1
|
|
214
|
+
print("%d.__________ CONFIGURE TIMESHIFT _____________________" % step)
|
|
215
|
+
|
|
216
|
+
default_timeshift_file_path = '/etc/timeshift/default.json'
|
|
217
|
+
if not os.path.exists(default_timeshift_file_path):
|
|
218
|
+
default_timeshift_file_path = '/etc/timeshift/timeshift.json'
|
|
219
|
+
if not os.path.exists(default_timeshift_file_path):
|
|
220
|
+
default_timeshift_file_path = '/etc/default/timeshift.json'
|
|
221
|
+
|
|
222
|
+
if not os.path.exists(default_timeshift_file_path):
|
|
223
|
+
print("Unable to find default TimeShift config! Skip TimeShift configuration.")
|
|
224
|
+
|
|
225
|
+
else:
|
|
226
|
+
|
|
227
|
+
with open(default_timeshift_file_path, 'r') as conf_f:
|
|
228
|
+
timeshift_conf = json.loads(conf_f.read())
|
|
229
|
+
|
|
230
|
+
timeshift_conf['backup_device_uuid'] = subprocess.check_output(
|
|
231
|
+
"lsblk -no UUID $(df -P /etc/SIMO/hub/settings.py | awk 'END{print $1}')",
|
|
232
|
+
shell=True
|
|
233
|
+
).decode()[:-1]
|
|
234
|
+
timeshift_conf['schedule_monthly'] = "true"
|
|
235
|
+
timeshift_conf['schedule_weekly'] = "true"
|
|
236
|
+
timeshift_conf['schedule_daily'] = "true"
|
|
237
|
+
|
|
238
|
+
# Must be copied to /etc/timeshift.json to work
|
|
239
|
+
with open('/etc/timeshift.json', 'w') as conf_f:
|
|
240
|
+
conf_f.write(json.dumps(timeshift_conf))
|
|
241
|
+
|
|
242
|
+
status = subprocess.call([
|
|
243
|
+
'/usr/bin/timeshift', '--create',
|
|
244
|
+
'--comments', '"Initial backup"', '--tags', 'M'
|
|
245
|
+
])
|
|
246
|
+
if status != 0:
|
|
247
|
+
print("Unable to start TimeShift")
|
|
248
|
+
|
|
249
|
+
with open(installed_flag_file_path, 'w') as f:
|
|
250
|
+
f.write(json.dumps(True))
|
|
251
|
+
|
|
252
|
+
print("--------------------------------------------------------------")
|
|
253
|
+
print("DONE!")
|
|
254
|
+
print("Your SIMO.io Hub is Up and running at: https://%s/" % get_ip())
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
if __name__ == "__main__":
|
|
258
|
+
sys.exit(init())
|
|
@@ -2,6 +2,7 @@ import os
|
|
|
2
2
|
import pwd
|
|
3
3
|
import grp
|
|
4
4
|
import subprocess
|
|
5
|
+
import pkg_resources
|
|
5
6
|
from django.conf import settings
|
|
6
7
|
from django.template.loader import render_to_string
|
|
7
8
|
|
|
@@ -58,7 +59,8 @@ prepare_mosquitto()
|
|
|
58
59
|
def update_auto_update():
|
|
59
60
|
import simo
|
|
60
61
|
auto_update_file_path = os.path.join(
|
|
61
|
-
os.path.dirname(simo.__file__), '
|
|
62
|
+
os.path.dirname(simo.__file__), 'management',
|
|
63
|
+
'auto_update.py'
|
|
62
64
|
)
|
|
63
65
|
st = os.stat(auto_update_file_path)
|
|
64
66
|
os.chmod(auto_update_file_path, st.st_mode | 0o111)
|
|
@@ -75,4 +77,22 @@ def update_auto_update():
|
|
|
75
77
|
cron_out.communicate(input=str.encode(auto_update_cron))
|
|
76
78
|
|
|
77
79
|
|
|
78
|
-
update_auto_update()
|
|
80
|
+
update_auto_update()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def maybe_update_to_latest_immediately():
|
|
84
|
+
from simo.core.tasks import update_latest_version_available, update
|
|
85
|
+
from simo.core.models import Instance
|
|
86
|
+
from simo.conf import dynamic_settings
|
|
87
|
+
update_latest_version_available()
|
|
88
|
+
if dynamic_settings['core__latest_version_available'] != \
|
|
89
|
+
pkg_resources.get_distribution('simo').version:
|
|
90
|
+
print("There is newer version, we should probably update!")
|
|
91
|
+
if not Instance.objects.all().count():
|
|
92
|
+
print("Yes let's do it asynchronously!")
|
|
93
|
+
return update.s()
|
|
94
|
+
print("Nope, we already have some instances running, "
|
|
95
|
+
"so we leave that for hub owners.")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
maybe_update_to_latest_immediately()
|