mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Yoloimplement some resources classes, not used for anything for now
This commit is contained in:
parent
fe6d9c2617
commit
859b3b6f12
1 changed files with 247 additions and 0 deletions
247
src/yunohost/utils/resources.py
Normal file
247
src/yunohost/utils/resources.py
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
""" License
|
||||||
|
|
||||||
|
Copyright (C) 2021 YUNOHOST.ORG
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program; if not, see http://www.gnu.org/licenses
|
||||||
|
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import copy
|
||||||
|
import psutil
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||||
|
from yunohost.utils.filesystem import free_space_in_directory
|
||||||
|
|
||||||
|
|
||||||
|
class AppResource:
|
||||||
|
|
||||||
|
def __init__(self, properties: Dict[str, Any], app_id: str, app_settings):
|
||||||
|
|
||||||
|
for key, value in self.default_properties.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
for key, value in properties:
|
||||||
|
setattr(self. key, value)
|
||||||
|
|
||||||
|
|
||||||
|
M = 1024 ** 2
|
||||||
|
G = 1024 * M
|
||||||
|
sizes = {
|
||||||
|
"10M": 10 * M,
|
||||||
|
"20M": 20 * M,
|
||||||
|
"40M": 40 * M,
|
||||||
|
"80M": 80 * M,
|
||||||
|
"100M": 100 * M,
|
||||||
|
"200M": 200 * M,
|
||||||
|
"400M": 400 * M,
|
||||||
|
"800M": 800 * M,
|
||||||
|
"1G": 1 * G,
|
||||||
|
"2G": 2 * G,
|
||||||
|
"4G": 4 * G,
|
||||||
|
"8G": 8 * G,
|
||||||
|
"10G": 10 * G,
|
||||||
|
"20G": 20 * G,
|
||||||
|
"40G": 40 * G,
|
||||||
|
"80G": 80 * G,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DiskAppResource(AppResource):
|
||||||
|
type = "disk"
|
||||||
|
|
||||||
|
default_properties = {
|
||||||
|
"space": "10M",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(self, *args, **kwargs)
|
||||||
|
# FIXME: better error handling
|
||||||
|
assert self.space in sizes
|
||||||
|
|
||||||
|
def provision_or_update(self, context: Dict):
|
||||||
|
|
||||||
|
if free_space_in_directory("/") <= sizes[self.space] \
|
||||||
|
or free_space_in_directory("/var") <= sizes[self.space]:
|
||||||
|
raise YunohostValidationError("Not enough disk space") # FIXME: i18n / better messaging
|
||||||
|
|
||||||
|
def deprovision(self, context: Dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RamAppResource(AppResource):
|
||||||
|
type = "ram"
|
||||||
|
|
||||||
|
default_properties = {
|
||||||
|
"build": "10M",
|
||||||
|
"runtime": "10M",
|
||||||
|
"include_swap": False
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(self, *args, **kwargs)
|
||||||
|
# FIXME: better error handling
|
||||||
|
assert self.build in sizes
|
||||||
|
assert self.runtime in sizes
|
||||||
|
assert isinstance(self.include_swap, bool)
|
||||||
|
|
||||||
|
def provision_or_update(self, context: Dict):
|
||||||
|
|
||||||
|
memory = psutil.virtual_memory().available
|
||||||
|
if self.include_swap:
|
||||||
|
memory += psutil.swap_memory().available
|
||||||
|
|
||||||
|
max_size = max(sizes[self.build], sizes[self.runtime])
|
||||||
|
|
||||||
|
if memory <= max_size:
|
||||||
|
raise YunohostValidationError("Not enough RAM/swap") # FIXME: i18n / better messaging
|
||||||
|
|
||||||
|
def deprovision(self, context: Dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WebpathAppResource(AppResource):
|
||||||
|
type = "webpath"
|
||||||
|
|
||||||
|
default_properties = {
|
||||||
|
"url": "__DOMAIN____PATH__"
|
||||||
|
}
|
||||||
|
|
||||||
|
def provision_or_update(self, context: Dict):
|
||||||
|
|
||||||
|
from yunohost.app import _assert_no_conflicting_apps
|
||||||
|
|
||||||
|
# Check the url is available
|
||||||
|
domain = context["app_settings"]["domain"]
|
||||||
|
path = context["app_settings"]["path"] or "/"
|
||||||
|
_assert_no_conflicting_apps(domain, path, ignore_app=context["app"])
|
||||||
|
context["app_settings"]["path"] = path
|
||||||
|
|
||||||
|
if context["app_action"] == "install":
|
||||||
|
# Initially, the .main permission is created with no url at all associated
|
||||||
|
# When the app register/books its web url, we also add the url '/'
|
||||||
|
# (meaning the root of the app, domain.tld/path/)
|
||||||
|
# and enable the tile to the SSO, and both of this should match 95% of apps
|
||||||
|
# For more specific cases, the app is free to change / add urls or disable
|
||||||
|
# the tile using the permission helpers.
|
||||||
|
permission_url(app + ".main", url="/", sync_perm=False)
|
||||||
|
user_permission_update(app + ".main", show_tile=True, sync_perm=False)
|
||||||
|
permission_sync_to_user()
|
||||||
|
|
||||||
|
def deprovision(self, context: Dict):
|
||||||
|
del context["app_settings"]["domain"]
|
||||||
|
del context["app_settings"]["path"]
|
||||||
|
|
||||||
|
|
||||||
|
class PortAppResource(AppResource):
|
||||||
|
type = "port"
|
||||||
|
|
||||||
|
default_properties = {
|
||||||
|
"value": 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
def _port_is_used(self, port):
|
||||||
|
|
||||||
|
# FIXME : this could be less brutal than two os.system ...
|
||||||
|
cmd1 = "ss --numeric --listening --tcp --udp | awk '{print$5}' | grep --quiet --extended-regexp ':%s$'" % port
|
||||||
|
# This second command is mean to cover (most) case where an app is using a port yet ain't currently using it for some reason (typically service ain't up)
|
||||||
|
cmd2 = f"grep -q \"port: '{port}'\" /etc/yunohost/apps/*/settings.yml"
|
||||||
|
return os.system(cmd1) == 0 and os.system(cmd2) == 0
|
||||||
|
|
||||||
|
def provision_or_update(self, context: str):
|
||||||
|
|
||||||
|
# Don't do anything if port already defined ?
|
||||||
|
if context["app_settings"].get("port"):
|
||||||
|
return
|
||||||
|
|
||||||
|
port = self.value
|
||||||
|
while self._port_is_used(port):
|
||||||
|
port += 1
|
||||||
|
|
||||||
|
context["app_settings"]["port"] = port
|
||||||
|
|
||||||
|
def deprovision(self, context: Dict):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class UserAppResource(AppResource):
|
||||||
|
type = "user"
|
||||||
|
|
||||||
|
default_properties = {
|
||||||
|
"username": "__APP__",
|
||||||
|
"home_dir": "/var/www/__APP__",
|
||||||
|
"use_shell": False,
|
||||||
|
"groups": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def provision_or_update(self, context: str):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def deprovision(self, context: Dict):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class InstalldirAppResource(AppResource):
|
||||||
|
type = "installdir"
|
||||||
|
|
||||||
|
default_properties = {
|
||||||
|
"dir": "/var/www/__APP__",
|
||||||
|
"alias": "final_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
def provision_or_update(self, context: Dict):
|
||||||
|
|
||||||
|
if context["app_action"] in ["install", "restore"]:
|
||||||
|
if os.path.exists(self.dir):
|
||||||
|
raise YunohostValidationError(f"Path {self.dir} already exists")
|
||||||
|
|
||||||
|
if "installdir" not in context["app_settings"]:
|
||||||
|
context["app_settings"]["installdir"] = self.dir
|
||||||
|
context["app_settings"][self.alias] = context["app_settings"]["installdir"]
|
||||||
|
|
||||||
|
def deprovision(self, context: Dict):
|
||||||
|
# FIXME: should it rm the directory during remove/deprovision ?
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DatadirAppResource(AppResource):
|
||||||
|
type = "datadir"
|
||||||
|
|
||||||
|
default_properties = {
|
||||||
|
"dir": "/home/yunohost.app/__APP__",
|
||||||
|
}
|
||||||
|
|
||||||
|
def provision_or_update(self, context: Dict):
|
||||||
|
if "datadir" not in context["app_settings"]:
|
||||||
|
context["app_settings"]["datadir"] = self.dir
|
||||||
|
|
||||||
|
def deprovision(self, context: Dict):
|
||||||
|
# FIXME: should it rm the directory during remove/deprovision ?
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DBAppResource(AppResource):
|
||||||
|
type = "db"
|
||||||
|
|
||||||
|
default_properties = {
|
||||||
|
"type": "mysql"
|
||||||
|
}
|
||||||
|
|
||||||
|
def provision_or_update(self, context: str):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def deprovision(self, context: Dict):
|
||||||
|
raise NotImplementedError()
|
Loading…
Add table
Reference in a new issue